The module list may be split into multiple lines, at arbitrary position. The parsing function now tries to handle that, though not in a very robust way, by means of heuristic that decides whether the next line is a continuation of the previous.
Closes #24.
Signed-off-by: Martin Milata mmilata@redhat.com --- lib/koops_stacktrace.c | 85 +++++++++++++++++++++++++++++++++++++++-------- tests/koops_stacktrace.at | 63 +++++++++++++++++++++++++++++++++-- tests/python/koops.py | 2 +- 3 files changed, 134 insertions(+), 16 deletions(-)
diff --git a/lib/koops_stacktrace.c b/lib/koops_stacktrace.c index a10d816..8964d04 100644 --- a/lib/koops_stacktrace.c +++ b/lib/koops_stacktrace.c @@ -144,6 +144,34 @@ sr_koops_stacktrace_parse(const char **input, return stacktrace; }
+static bool +module_list_continues(const char *input) +{ + char *end = strchr(input, '\n'); + + /* Module list shouldn't be on the last line */ + if (!end || input == end) + return false; + + size_t line_len = end - input; + char *line = sr_malloc(line_len + 1); + memcpy(line, input, line_len); + line[line_len] = '\0'; + + bool res = true; + + /* Usually follows the module list */ + if (strstr(line, "Pid: ")) + res = false; + /* There should be no timestamp in the continuation */ + else if (sr_koops_skip_timestamp(&input)) + res = false; + /* Other conditions may need to be added */ + + free(line); + return res; +} + char ** sr_koops_stacktrace_parse_modules(const char **input) { @@ -157,27 +185,58 @@ sr_koops_stacktrace_parse_modules(const char **input) if (!sr_skip_string(&local_input, "Modules linked in:")) return NULL;
- sr_skip_char_span(&local_input, " \t"); + int ws = sr_skip_char_span(&local_input, " \t");
int result_size = 20, result_offset = 0; char **result = sr_malloc(result_size * sizeof(char*));
char *module; - bool success; - while ((success = sr_parse_char_cspan(&local_input, " \t\n[", &module))) + while (true) { - // result_size is lowered by 1 because we need to terminate - // the list by a NULL pointer. - if (result_offset == result_size - 1) + if (sr_parse_char_cspan(&local_input, " \t\n[", &module)) { - result_size *= 2; - result = sr_realloc(result, - result_size * sizeof(char*)); + // result_size is lowered by 1 because we need to terminate + // the list by a NULL pointer. + if (result_offset == result_size - 1) + { + result_size *= 2; + result = sr_realloc(result, + result_size * sizeof(char*)); + } + + result[result_offset] = module; + ++result_offset; + ws = sr_skip_char_span(&local_input, " \t"); } - - result[result_offset] = module; - ++result_offset; - sr_skip_char_span(&local_input, " \t"); + else if(*local_input == '\n') + { + /* We're at the end of the line. Check whether the next line might + * be a continuation of the module list. */ + if (!module_list_continues(local_input + 1)) + break; + + local_input++; + + /* If the next line does not start with space and there wasn't + * any space before the newline either, then the last module was + * split into two parts and we need to read the rest */ + if (*local_input != ' ' && ws == 0 && result_offset > 0) + { + char *therest; + if (!sr_parse_char_cspan(&local_input, " \t\n[", &therest)) + { + break; /* wtf? */ + } + + char *tmp = sr_asprintf("%s%s", result[result_offset-1], therest); + free(result[result_offset-1]); + result[result_offset-1] = tmp; + } + + ws = sr_skip_char_span(&local_input, " \t"); + } + else + break; }
result[result_offset] = NULL; diff --git a/tests/koops_stacktrace.at b/tests/koops_stacktrace.at index d30e8b9..672b0fc 100644 --- a/tests/koops_stacktrace.at +++ b/tests/koops_stacktrace.at @@ -22,7 +22,6 @@ check(bool success,
if (modules) { - assert(*input == '\0'); int offset = 0; while (modules[offset]) { @@ -55,6 +54,38 @@ main(void) check(true, "[110417.280431] Modules linked in: vfat fat usb_storage", modules); + + check(true, + "[110417.280431] Modules linked in: vfat fat usb_storage [last unloaded: scsi_wait_scan]\n", + modules); + + check(true, + "[110417.280431] Modules linked in: vfat fat usb_storage \n[last unloaded: scsi_wait_scan]\n", + modules); + + check(true, + "[110417.280431] Modules linked in: vfat fat usb_storage\n [last unloaded: scsi_wait_scan]\n", + modules); + + check(true, + "[110417.280431] Modules linked in: vfat fat usb_st\norage [last unloaded: scsi_wait_scan]\n", + modules); + + check(true, + "[110417.280431] Modules linked in: vfat fat \nusb_storage [last unloaded: scsi_wait_scan]\n", + modules); + + check(true, + "[110417.280431] Modules linked in: vfat fat\n usb_storage [last unloaded: scsi_wait_scan]\n", + modules); + + check(true, + "[110417.280431] Modules linked in: vfat fat usb_storage\n" + "Pid: 31337, comm: ksoftirqd/2\n", + modules); + + check(false, "[110417.280434] I hate autotest", modules); + return 0; } ]]) @@ -299,7 +330,35 @@ main(void) " , "bluetooth"\n" " , "iwlwifi"\n" " , "snd_pcm"\n" - " , "parport_pc" ]\n" + " , "parport_pc"\n" + " , "joydev"\n" + " , "parport"\n" + " , "snd_timer"\n" + " , "microcode"\n" + " , "mac80211"\n" + " , "snd"\n" + " , "soundcore"\n" + " , "snd_page_alloc"\n" + " , "i2c_i801"\n" + " , "cfg80211"\n" + " , "iTCO_wdt"\n" + " , "iTCO_vendor_support"\n" + " , "rfkill"\n" + " , "e1000e"\n" + " , "sunrpc"\n" + " , "xts"\n" + " , "gf128mul"\n" + " , "dm_crypt"\n" + " , "sdhci_pci"\n" + " , "sdhci"\n" + " , "mmc_core"\n" + " , "wmi"\n" + " , "i915"\n" + " , "drm_kms_helper"\n" + " , "drm"\n" + " , "i2c_algo_bit"\n" + " , "i2c_core"\n" + " , "video" ]\n" ", "frames":\n" " [ { "address": 18446744071579204319\n" " , "reliable": true\n" diff --git a/tests/python/koops.py b/tests/python/koops.py index b702548..b343979 100755 --- a/tests/python/koops.py +++ b/tests/python/koops.py @@ -11,7 +11,7 @@ except ImportError:
path = '../kerneloopses/rhbz-827868' frames_expected = 32 -mods_expected = 91 +mods_expected = 119
if not os.path.isfile(path): path = '../' + path