3 * Copyright 2007, Intel Corporation
5 * This file is part of corewatcher.org
7 * This program file is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; version 2 of the License.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * You should have received a copy of the GNU General Public License
17 * along with this program in a file named COPYING; if not, write to the
18 * Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301 USA
23 * Arjan van de Ven <arjan@linux.intel.com>
24 * William Douglas <william.douglas@intel.com>
34 #include <asm/unistd.h>
35 #include <sys/types.h>
43 #include "corewatcher.h"
48 /* Always pick up the processing_mtx and then the
49 processing_queue_mtx, reverse for setting down */
50 /* Always pick up the gdb_mtx and then the
51 processing_queue_mtx, reverse for setting down */
52 /* Always pick up the processing_mtx and then the
53 gdb_mtx, reverse for setting down */
54 /* so order for pick up should be:
55 processing_mtx -> gdb_mtx -> processing_queue_mtx
56 and the reverse for setting down */
57 static pthread_mutex_t processing_queue_mtx = PTHREAD_MUTEX_INITIALIZER;
58 static char *processing_queue[MAX_PROCESSING_OOPS];
59 static pthread_mutex_t gdb_mtx = PTHREAD_MUTEX_INITIALIZER;
63 static char *get_release(void)
69 file = fopen("/etc/os-release", "r");
71 line = strdup("Unknown");
76 if (getline(&line, &dummy, file) == -1)
78 if (strstr(line, "VERSION_ID=")) {
81 c = strchr(line, '\n');
84 c = strdup(&line[11]);
95 line = strdup("Unknown");
100 static char *set_wrapped_app(char *line)
102 char *dline = NULL, *app = NULL, *appfile = NULL, *abs_path = NULL;
104 char app_folder[] = "/usr/share/";
110 dline = strdup(line);
112 app = strtok(dline, delim);
114 if (strcmp(app, "--app") == 0) {
115 app = strtok(NULL, delim);
118 app = strtok(NULL, delim);
121 goto cleanup_set_wrapped_app;
122 r = asprintf(&abs_path, "%s%s", app_folder, app);
125 goto cleanup_set_wrapped_app;
126 } else if (((unsigned int)r) != strlen(app_folder) + strlen(app)) {
127 goto cleanup_set_wrapped_app;
130 appfile = find_executable(abs_path);
132 cleanup_set_wrapped_app:
139 * Scan core dump in case a wrapper was used
140 * to run the process and get the actual binary name
142 static char *wrapper_scan(char *command)
144 char *line = NULL, *appfile = NULL;
147 file = popen(command, "r");
151 while (!feof(file)) {
155 ret = getline(&line, &size, file);
161 if (strstr(line, "Core was generated by") &&
162 strstr(line, "--app")) {
163 /* attempt to update appfile */
164 appfile = set_wrapped_app(line);
176 * Strip the directories from the path
179 char *strip_directories(char *fullpath)
181 char *dfile = NULL, *c1 = NULL, *c2 = NULL, *r = NULL;
187 dfile = strdup(fullpath);
191 c1 = strtok(dfile, delim);
194 c1 = strtok(NULL, delim);
205 * Move corefile from /tmp to core_folder.
206 * Add extension if given and attempt to create core_folder.
208 int move_core(char *fullpath, char *extension)
210 char *corefn = NULL, newpath[8192];
212 if (!core_folder || !fullpath)
215 if (!(corefn = strip_directories(fullpath))) {
220 if (!mkdir(core_folder, S_IRWXU | S_IRWXG | S_IRWXO)
221 && errno != EEXIST) {
227 snprintf(newpath, 8192, "%s%s.%s", core_folder, corefn, extension);
229 snprintf(newpath, 8192, "%s%s", core_folder, corefn);
232 rename(fullpath, newpath);
238 * Finds the full path for the application that crashed,
239 * and depending on what opted_in was configured as will:
240 * opted_in 2 (always submit) -> move file to core_folder
241 * to be processed further
242 * opted_in 1 (ask user) -> ask user if we should submit
243 * the crash and add to asked_oops hash so we don't get
244 * called again for this corefile
245 * opted_in 0 (don't submit) -> do nothing
247 * Picks up and sets down the asked_mtx.
249 static char *get_appfile(char *fullpath)
251 char *appname = NULL, *appfile = NULL;
256 appname = find_coredump(fullpath);
260 /* don't try and do anything for rpm, gdb or corewatcher crashes */
261 if (!(strcmp(appname, "rpm") && strcmp(appname, "gdb") && strcmp(appname, "corewatcher")))
264 appfile = find_executable(appname);
265 /* appname no longer used, so free it as it was strdup'd */
270 move_core(fullpath, "to-process");
276 * Use GDB to extract backtrace information from corefile
278 static struct oops *extract_core(char *fullpath, char *appfile)
280 struct oops *oops = NULL;
282 char *command = NULL, *h1 = NULL, *c1 = NULL, *c2 = NULL, *line = NULL, *text = NULL, *at = NULL;
283 char *m1 = NULL, *m2 = NULL;
285 char *badchar = NULL;
286 char *release = get_release();
287 int parsing_maps = 0;
289 if (asprintf(&command, "LANG=C gdb --batch -f %s %s -x /etc/corewatcher/gdb.command 2> /dev/null", appfile, fullpath) == -1)
292 if ((at = wrapper_scan(command))) {
304 h1 = strdup("Unknown");
306 file = popen(command, "r");
308 while (file && !feof(file)) {
313 ret = getline(&line, &size, file);
319 if (strncmp(line, "From", 4) == 0) {
324 if (!parsing_maps) { /* parsing backtrace */
328 fixup: /* gdb outputs some 0x1a's which break XML */
329 badchar = memchr(line, 0x1a, size);
337 if (asprintf(&c1, "%s %s", c2, line) == -1)
341 /* keep going even if asprintf has errors */
342 ret = asprintf(&c1, " %s", line);
344 } else { /* parsing maps */
348 if (asprintf(&m1, "%s %s", m2, line) == -1)
352 /* keep going even if asprintf has errors */
353 ret = asprintf(&m1, " %s", line);
363 ret = asprintf(&text,
376 oops = malloc(sizeof(struct oops));
381 memset(oops, 0, sizeof(struct oops));
382 oops->application = strdup(appfile);
384 oops->filename = strdup(fullpath);
389 * filename is of the form core_XXXX[.blah]
390 * we need to get the pid out as we want
391 * output of the form XXXX[.ext]
393 char *get_core_filename(char *filename, char *ext)
395 char *pid = NULL, *c = NULL, *s = NULL, *detail_filename = NULL;
400 if (!(s = strstr(filename, "_")))
405 /* causes valgrind whining because we copy from middle of a string */
406 if (!(pid = strdup(s)))
409 c = strstr(pid, ".");
415 /* causes valgrind whining because we copy from middle of a string */
416 if ((asprintf(&detail_filename, "%s%s.%s", core_folder, pid, ext)) == -1) {
421 /* causes valgrind whining because we copy from middle of a string */
422 if ((asprintf(&detail_filename, "%s%s", core_folder, pid)) == -1) {
429 return detail_filename;
433 * Write the backtrace from the core file into a text
434 * file named after the pid
436 static void write_core_detail_file(char *filename, char *text)
439 char *detail_filename = NULL;
441 if (!filename || !text)
444 if (!(detail_filename = get_core_filename(filename, "txt")))
447 if ((fd = open(detail_filename, O_WRONLY | O_CREAT | O_TRUNC, 0)) != -1) {
448 if(write(fd, text, strlen(text)) >= 0)
451 unlink(detail_filename);
454 free(detail_filename);
458 * Removes corefile (core.XXXX) from the processing_queue.
460 * Expects the processing_queue_mtx to be held.
462 static void remove_from_processing_queue(void)
464 free(processing_queue[head]);
465 processing_queue[head++] = NULL;
472 * Removes file from processing_oops hash based on pid.
473 * Extracts pid from the fullpath such that
474 * /home/user/core.pid will be tranformed into just the pid.
476 * Expects the lock on the given hash to be held.
478 void remove_pid_from_hash(char *fullpath, GHashTable *ht)
480 char *c1 = NULL, *c2 = NULL;
482 if (!(c1 = strip_directories(fullpath)))
485 if (!(c2 = get_core_filename(c1, NULL))) {
492 g_hash_table_remove(ht, c2);
498 * Common function for processing core
499 * files to generate oops structures
501 static struct oops *process_common(char *fullpath)
503 struct oops *oops = NULL;
504 char *appname = NULL, *appfile = NULL;
506 if(!(appname = find_coredump(fullpath))) {
510 if (!(appfile = find_executable(appname))) {
516 if (!(oops = extract_core(fullpath, appfile))) {
527 * Processes .to-process core files.
528 * Also creates the pid.txt file and adds
529 * the oops struct to the submit queue
531 * Picks up and sets down the gdb_mtx and
532 * picks up and sets down the processing_queue_mtx.
533 * (held at the same time in that order)
534 * Also will pick up and sets down the queued_mtx.
536 static void *process_new(void __unused *vp)
538 struct oops *oops = NULL;
539 char *procfn = NULL, *corefn = NULL, *fullpath = NULL;
541 pthread_mutex_lock(&core_status.processing_mtx);
542 pthread_mutex_lock(&gdb_mtx);
543 pthread_mutex_lock(&processing_queue_mtx);
545 if (!(fullpath = processing_queue[head])) {
546 /* something went quite wrong */
547 pthread_mutex_unlock(&processing_queue_mtx);
548 pthread_mutex_unlock(&gdb_mtx);
549 pthread_mutex_unlock(&core_status.processing_mtx);
553 if (!(corefn = strip_directories(fullpath)))
554 goto clean_process_new;
556 if (!(procfn = replace_name(fullpath, ".to-process", ".processed")))
557 goto clean_process_new;
559 if (!(oops = process_common(fullpath)))
560 goto clean_process_new;
562 if (!(oops->detail_filename = get_core_filename(corefn, "txt")))
563 goto clean_process_new;
565 if (rename(fullpath, procfn))
566 goto clean_process_new;
568 free(oops->filename);
569 oops->filename = procfn;
571 remove_from_processing_queue();
573 pthread_mutex_unlock(&processing_queue_mtx);
574 pthread_mutex_unlock(&gdb_mtx);
575 pthread_mutex_unlock(&core_status.processing_mtx);
577 write_core_detail_file(corefn, oops->text);
579 pthread_mutex_lock(&core_status.queued_mtx);
580 queue_backtrace(oops);
581 pthread_mutex_unlock(&core_status.queued_mtx);
583 /* don't need to free procfn because was set to oops->filename and that gets free'd */
589 remove_pid_from_hash(fullpath, core_status.processing_oops);
590 remove_from_processing_queue();
592 procfn = NULL; /* don't know if oops->filename == procfn so be safe */
595 pthread_mutex_unlock(&processing_queue_mtx);
596 pthread_mutex_unlock(&gdb_mtx);
597 pthread_mutex_unlock(&core_status.processing_mtx);
602 * Reprocesses .processed core files.
604 * Picks up and sets down the gdb_mtx.
605 * Picks up and sets down the processing_queue_mtx.
606 * (held at the same time in that order)
607 * Also will pick up and sets down the queued_mtx.
609 static void *process_old(void __unused *vp)
611 struct oops *oops = NULL;
612 char *corefn = NULL, *fullpath = NULL;
614 pthread_mutex_lock(&gdb_mtx);
615 pthread_mutex_lock(&processing_queue_mtx);
617 if (!(fullpath = processing_queue[head])) {
618 /* something went quite wrong */
619 pthread_mutex_unlock(&processing_queue_mtx);
620 pthread_mutex_unlock(&gdb_mtx);
624 if (!(corefn = strip_directories(fullpath)))
625 goto clean_process_old;
627 if (!(oops = process_common(fullpath)))
628 goto clean_process_old;
630 if (!(oops->detail_filename = get_core_filename(corefn, "txt")))
631 goto clean_process_old;
633 remove_from_processing_queue();
635 pthread_mutex_unlock(&processing_queue_mtx);
636 pthread_mutex_unlock(&gdb_mtx);
638 pthread_mutex_lock(&core_status.queued_mtx);
639 queue_backtrace(oops);
640 pthread_mutex_unlock(&core_status.queued_mtx);
647 remove_pid_from_hash(fullpath, core_status.processing_oops);
648 remove_from_processing_queue();
651 pthread_mutex_unlock(&processing_queue_mtx);
652 pthread_mutex_unlock(&gdb_mtx);
657 * Adds corefile (based on pid) to the processing_oops
658 * hash table if it is not already there, then
659 * tries to add to the processing_queue.
661 * Picks up and sets down the processing_mtx.
662 * Picks up and sets down the processing_queue_mtx.
664 static int add_to_processing(char *fullpath)
666 char *c1 = NULL, *c2 = NULL, *fp = NULL;
671 if (!(fp = strdup(fullpath)))
672 goto clean_add_to_processing;
674 if (!(c1 = get_core_filename(fp, NULL)))
675 goto clean_add_to_processing;
677 if (!(c2 = strip_directories(c1)))
678 goto clean_add_to_processing;
683 pthread_mutex_lock(&core_status.processing_mtx);
684 if (g_hash_table_lookup(core_status.processing_oops, c2)) {
685 pthread_mutex_unlock(&core_status.processing_mtx);
686 goto clean_add_to_processing;
689 pthread_mutex_lock(&processing_queue_mtx);
690 if (processing_queue[tail]) {
691 pthread_mutex_unlock(&processing_queue_mtx);
692 pthread_mutex_unlock(&core_status.processing_mtx);
693 goto clean_add_to_processing;
696 g_hash_table_insert(core_status.processing_oops, c2, c2);
697 processing_queue[tail++] = fp;
701 pthread_mutex_unlock(&processing_queue_mtx);
702 pthread_mutex_unlock(&core_status.processing_mtx);
704 clean_add_to_processing:
712 * Entry for processing new core files.
714 static void process_corefile(char *fullpath)
719 r = add_to_processing(fullpath);
724 if (pthread_create(&thrd, NULL, process_new, NULL))
725 fprintf(stderr, "Couldn't start up gdb extract core thread\n");
729 * Entry for processing already seen core files.
731 static void reprocess_corefile(char *fullpath)
736 r = add_to_processing(fullpath);
741 if (pthread_create(&thrd, NULL, process_old, NULL))
742 fprintf(stderr, "Couldn't start up gdb extract core thread\n");
745 int scan_corefolders(void __unused *unused)
748 struct dirent *entry = NULL;
749 char *fullpath = NULL, *appfile = NULL;
750 char tmp_folder[] = "/tmp/";
753 pthread_mutex_init(&core_status.processing_mtx, NULL);
754 pthread_mutex_init(&core_status.queued_mtx, NULL);
755 pthread_mutex_init(&core_status.asked_mtx, NULL);
757 dir = opendir(tmp_folder);
761 fprintf(stderr, "+ scanning %s...\n", tmp_folder);
766 entry = readdir(dir);
767 if (!entry || !entry->d_name)
769 if (entry->d_name[0] == '.')
771 if (strncmp(entry->d_name, "core_", 5))
774 /* matched core_#### where #### is the pid of the process */
775 r = asprintf(&fullpath, "%s%s", tmp_folder, entry->d_name);
779 } else if (((unsigned int)r) != strlen(tmp_folder) + strlen(entry->d_name)) {
782 /* already found, waiting for response from user */
783 pthread_mutex_lock(&core_status.asked_mtx);
784 if (g_hash_table_lookup(core_status.asked_oops, fullpath)) {
785 pthread_mutex_unlock(&core_status.asked_mtx);
788 pthread_mutex_unlock(&core_status.asked_mtx);
789 fprintf(stderr, "+ Looking at %s\n", fullpath);
790 appfile = get_appfile(fullpath);
803 dir = opendir(core_folder);
805 if (!mkdir(core_folder, S_IRWXU | S_IRWXG | S_IRWXO)
806 && errno != EEXIST) {
811 fprintf(stderr, "+ scanning %s...\n", core_folder);
816 entry = readdir(dir);
817 if (!entry || !entry->d_name)
819 if (entry->d_name[0] == '.')
821 if (!strstr(entry->d_name, "process"))
824 r = asprintf(&fullpath, "%s%s", core_folder, entry->d_name);
828 } else if (((unsigned int)r) != strlen(core_folder) + strlen(entry->d_name)) {
832 fprintf(stderr, "+ Looking at %s\n", fullpath);
833 if (strstr(fullpath, "to-process"))
834 process_corefile(fullpath);
836 reprocess_corefile(fullpath);