Code

Generate crash reports on die in fast-import
authorShawn O. Pearce <spearce@spearce.org>
Fri, 3 Aug 2007 06:00:37 +0000 (02:00 -0400)
committerShawn O. Pearce <spearce@spearce.org>
Sun, 19 Aug 2007 07:42:41 +0000 (03:42 -0400)
As fast-import is quite strict about its input and die()'s anytime
something goes wrong it can be difficult for a frontend developer
to troubleshoot why fast-import rejected their input, or to even
determine what input command it rejected.

This change introduces a custom handler for Git's die() routine.
When we receive a die() for any reason (fast-import or a lower level
core Git routine we called) the error is first dumped onto stderr
and then a more extensive crash report file is prepared in GIT_DIR.
Finally we exit the process with status 128, just like the stock
builtin die handler.

An internal flag is set to prevent any further die()'s that may be
invoked during the crash report generator from causing us to enter
into an infinite loop.  We shouldn't die() from our crash report
handler, but just in case someone makes a future code change we are
prepared to gaurd against small mistakes turning into huge problems
for the end-user.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
fast-import.c

index 429ab491bd3dc6d084c7301ec4fc2bb5afa3ed04..71b89e2036eeacf265af470a76706add51f073d2 100644 (file)
@@ -338,6 +338,98 @@ static int unread_command_buf;
 static uintmax_t next_mark;
 static struct dbuf new_data;
 
+static void write_branch_report(FILE *rpt, struct branch *b)
+{
+       fprintf(rpt, "%s:\n", b->name);
+
+       fprintf(rpt, "  status      :");
+       if (b->active)
+               fputs(" active", rpt);
+       if (b->branch_tree.tree)
+               fputs(" loaded", rpt);
+       if (is_null_sha1(b->branch_tree.versions[1].sha1))
+               fputs(" dirty", rpt);
+       fputc('\n', rpt);
+
+       fprintf(rpt, "  tip commit  : %s\n", sha1_to_hex(b->sha1));
+       fprintf(rpt, "  old tree    : %s\n", sha1_to_hex(b->branch_tree.versions[0].sha1));
+       fprintf(rpt, "  cur tree    : %s\n", sha1_to_hex(b->branch_tree.versions[1].sha1));
+       fprintf(rpt, "  commit clock: %" PRIuMAX "\n", b->last_commit);
+
+       fputs("  last pack   : ", rpt);
+       if (b->pack_id < MAX_PACK_ID)
+               fprintf(rpt, "%u", b->pack_id);
+       fputc('\n', rpt);
+
+       fputc('\n', rpt);
+}
+
+static void write_crash_report(const char *err, va_list params)
+{
+       char *loc = git_path("fast_import_crash_%d", getpid());
+       FILE *rpt = fopen(loc, "w");
+       struct branch *b;
+       unsigned long lu;
+
+       if (!rpt) {
+               error("can't write crash report %s: %s", loc, strerror(errno));
+               return;
+       }
+
+       fprintf(stderr, "fast-import: dumping crash report to %s\n", loc);
+
+       fprintf(rpt, "fast-import crash report:\n");
+       fprintf(rpt, "    fast-import process: %d\n", getpid());
+       fprintf(rpt, "    parent process     : %d\n", getppid());
+       fprintf(rpt, "    at %s\n", show_date(time(NULL), 0, DATE_LOCAL));
+       fputc('\n', rpt);
+
+       fputs("fatal: ", rpt);
+       vfprintf(rpt, err, params);
+       fputc('\n', rpt);
+
+       fputc('\n', rpt);
+       fputs("Active Branch LRU\n", rpt);
+       fputs("-----------------\n", rpt);
+       fprintf(rpt, "    active_branches = %lu cur, %lu max\n",
+               cur_active_branches,
+               max_active_branches);
+       fputc('\n', rpt);
+       fputs("  pos  clock name\n", rpt);
+       fputs("  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", rpt);
+       for (b = active_branches, lu = 0; b; b = b->active_next_branch)
+               fprintf(rpt, "  %2lu) %6" PRIuMAX" %s\n",
+                       ++lu, b->last_commit, b->name);
+
+       fputc('\n', rpt);
+       fputs("Inactive Branches\n", rpt);
+       fputs("-----------------\n", rpt);
+       for (lu = 0; lu < branch_table_sz; lu++) {
+               for (b = branch_table[lu]; b; b = b->table_next_branch)
+                       write_branch_report(rpt, b);
+       }
+
+       fputc('\n', rpt);
+       fputs("-------------------\n", rpt);
+       fputs("END OF CRASH REPORT\n", rpt);
+       fclose(rpt);
+}
+
+static NORETURN void die_nicely(const char *err, va_list params)
+{
+       static int zombie;
+
+       fputs("fatal: ", stderr);
+       vfprintf(stderr, err, params);
+       fputc('\n', stderr);
+
+       if (!zombie) {
+               zombie = 1;
+               write_crash_report(err, params);
+       }
+
+       exit(128);
+}
 
 static void alloc_objects(unsigned int cnt)
 {
@@ -2233,6 +2325,7 @@ int main(int argc, const char **argv)
 
        prepare_packed_git();
        start_packfile();
+       set_die_routine(die_nicely);
        for (;;) {
                read_next_command();
                if (command_buf.eof)