summaryrefslogtreecommitdiffstats
path: root/scripts/ksymoops/ksyms.c
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/ksymoops/ksyms.c')
-rw-r--r--scripts/ksymoops/ksyms.c294
1 files changed, 294 insertions, 0 deletions
diff --git a/scripts/ksymoops/ksyms.c b/scripts/ksymoops/ksyms.c
new file mode 100644
index 000000000..608254ad3
--- /dev/null
+++ b/scripts/ksymoops/ksyms.c
@@ -0,0 +1,294 @@
+/*
+ ksyms.c.
+
+ Process ksyms for ksymoops.
+
+ Copyright Keith Owens <kaos@ocs.com.au>.
+ Released under the GNU Public Licence, Version 2.
+
+ Fri Nov 6 10:38:42 EST 1998
+ Version 0.6b
+ Remove false warnings when comparing ksyms and lsmod.
+
+ Tue Nov 3 02:31:01 EST 1998
+ Version 0.6
+ Read lsmod (/proc/modules).
+ Move "Using_Version" copy to map.c.
+
+ Wed Oct 28 13:47:23 EST 1998
+ Version 0.4
+ Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <malloc.h>
+#include <string.h>
+
+/* Scan one line from ksyms. Split lines into the base symbols and the module
+ * symbols. Separate ss for base and each module.
+ */
+static void scan_ksyms_line(const char *line)
+{
+ int i;
+ char **string = NULL;
+ SYMBOL_SET *ssp;
+ static char *prev_module = NULL;
+ static regex_t re_ksyms;
+ static regmatch_t *re_ksyms_pmatch;
+ static char const procname[] = "scan_ksyms_line";
+
+ /* ksyms: address, symbol, optional module */
+ re_compile(&re_ksyms,
+ "^([0-9a-fA-F]{4,}) +([^ \t]+)([ \t]+\\[([^ ]+)\\])?$",
+ REG_NEWLINE|REG_EXTENDED,
+ &re_ksyms_pmatch);
+
+ i = regexec(&re_ksyms, line,
+ re_ksyms.re_nsub+1, re_ksyms_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+ if (i)
+ return;
+
+ /* string [1] - address, [2] - symbol, [3] - white space+module,
+ * [4] - module.
+ */
+ re_strings(&re_ksyms, line, re_ksyms_pmatch, &string);
+ if (string[4]) {
+ if (!prev_module || strcmp(prev_module, string[4])) {
+ /* start of a new module in ksyms */
+ ++ss_ksyms_modules;
+ ss_ksyms_module = realloc(ss_ksyms_module,
+ ss_ksyms_modules*sizeof(*ss_ksyms_module));
+ if (!ss_ksyms_module)
+ malloc_error("realloc ss_ksyms_module");
+ ssp = ss_ksyms_module+ss_ksyms_modules-1;
+ ss_init(ssp, string[4]);
+ prev_module = strdup(string[4]);
+ if (!prev_module)
+ malloc_error("strdup prev_module");
+ }
+ ssp = ss_ksyms_module+ss_ksyms_modules-1;
+ }
+ else
+ ssp = &ss_ksyms_base;
+ add_symbol(ssp, string[1], ' ', 1, string[2]);
+ re_strings_free(&re_ksyms, &string);
+}
+
+/* Read the symbols from ksyms. */
+void read_ksyms(const char *ksyms)
+{
+ FILE *f;
+ char *line = NULL;
+ int i, size;
+ static char const procname[] = "read_ksyms";
+
+ if (!ksyms)
+ return;
+ ss_init(&ss_ksyms_base, "ksyms_base");
+ if (debug)
+ fprintf(stderr, "DEBUG: %s %s\n", procname, ksyms);
+
+ if (!regular_file(ksyms, procname))
+ return;
+
+ if (!(f = fopen_local(ksyms, "r", procname)))
+ return;
+
+ while (fgets_local(&line, &size, f, procname))
+ scan_ksyms_line(line);
+
+ fclose_local(f, procname);
+ free(line);
+
+ for (i = 0; i < ss_ksyms_modules; ++i) {
+ ss_sort_na(ss_ksyms_module+i);
+ extract_Version(ss_ksyms_module+i);
+ }
+ if (ss_ksyms_base.used) {
+ ss_sort_na(&ss_ksyms_base);
+ extract_Version(&ss_ksyms_base);
+ }
+ else {
+ fprintf(stderr,
+ "Warning, no kernel symbols in ksyms, is %s a valid "
+ "ksyms file?\n",
+ ksyms);
+ ++warnings;
+ }
+
+ if (debug > 1) {
+ for (i = 0; i < ss_ksyms_modules; ++i) {
+ fprintf(stderr,
+ "DEBUG: %s %s used %d out of %d entries\n",
+ procname,
+ ss_ksyms_module[i].source,
+ ss_ksyms_module[i].used,
+ ss_ksyms_module[i].alloc);
+ }
+ fprintf(stderr,
+ "DEBUG: %s %s used %d out of %d entries\n",
+ procname, ss_ksyms_base.source, ss_ksyms_base.used,
+ ss_ksyms_base.alloc);
+ }
+}
+
+/* Map each ksyms module entry to the corresponding object entry. Tricky,
+ * see the comments in the docs about needing a unique symbol in each
+ * module.
+ */
+static void map_ksym_to_module(SYMBOL_SET *ss)
+{
+ int i, j, matches;
+ char *name = NULL;
+
+ for (i = 0; i < ss->used; ++i) {
+ matches = 0;
+ for (j = 0; j < ss_objects; ++j) {
+ name = (ss->symbol)[i].name;
+ if (find_symbol_name(ss_object+j, name, NULL)) {
+ ++matches;
+ ss->related = ss_object+j;
+ }
+ }
+ if (matches == 1)
+ break; /* unique symbol over all objects */
+ ss->related = NULL; /* keep looking */
+ }
+ if (!(ss->related)) {
+ fprintf(stderr,
+ "Warning: cannot match loaded module %s to any "
+ "module object. Trace may not be reliable.\n",
+ ss->source);
+ ++warnings;
+ }
+ else if (debug)
+ fprintf(stderr,
+ "DEBUG: ksyms %s matches to %s based on unique "
+ "symbol %s\n",
+ ss->source, ss->related->source, name);
+}
+
+/* Map all ksyms module entries to their corresponding objects */
+void map_ksyms_to_modules(void)
+{
+ int i;
+ SYMBOL_SET *ss, *ssc;
+
+ for (i = 0; i < ss_ksyms_modules; ++i) {
+ ss = ss_ksyms_module+i;
+ map_ksym_to_module(ss);
+ if (ss->related) {
+ ssc = adjust_object_offsets(ss);
+ compare_maps(ss, ssc, 1);
+ }
+ }
+}
+
+/* Read the modules from lsmod. */
+void read_lsmod(const char *lsmod)
+{
+ FILE *f;
+ char *line = NULL;
+ int i, size;
+ char **string = NULL;
+ static regex_t re_lsmod;
+ static regmatch_t *re_lsmod_pmatch;
+ static char const procname[] = "read_lsmod";
+
+ if (!lsmod)
+ return;
+ ss_init(&ss_lsmod, "lsmod");
+ if (debug)
+ fprintf(stderr, "DEBUG: %s %s\n", procname, lsmod);
+
+ if (!regular_file(lsmod, procname))
+ return;
+
+ if (!(f = fopen_local(lsmod, "r", procname)))
+ return;
+
+ /* lsmod: module, size, use count, optional used by */
+ re_compile(&re_lsmod,
+ "^"
+ "[ \t]*([^ \t]+)" /* 1 module */
+ "[ \t]*([^ \t]+)" /* 2 size */
+ "[ \t]*([^ \t]+)" /* 3 count */
+ "[ \t]*(.*)" /* 4 used by */
+ "$",
+ REG_NEWLINE|REG_EXTENDED,
+ &re_lsmod_pmatch);
+
+ while (fgets_local(&line, &size, f, procname)) {
+ i = regexec(&re_lsmod, line,
+ re_lsmod.re_nsub+1, re_lsmod_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+ if (i)
+ continue;
+ re_strings(&re_lsmod, line, re_lsmod_pmatch, &string);
+ add_symbol(&ss_lsmod, string[2], ' ', 1, string[1]);
+ }
+
+ fclose_local(f, procname);
+ free(line);
+ re_strings_free(&re_lsmod, &string);
+ if (ss_lsmod.used)
+ ss_sort_na(&ss_lsmod);
+ else {
+ fprintf(stderr,
+ "Warning, no symbols in lsmod, is %s a valid "
+ "lsmod file?\n",
+ lsmod);
+ ++warnings;
+ }
+
+ if (debug > 1)
+ fprintf(stderr,
+ "DEBUG: %s %s used %d out of %d entries\n",
+ procname, ss_lsmod.source, ss_lsmod.used,
+ ss_lsmod.alloc);
+}
+
+/* Compare modules from ksyms against module list in lsmod and vice versa.
+ * There is one ss_ for each ksyms module and a single ss_lsmod to cross
+ * check.
+ */
+void compare_ksyms_lsmod(void)
+{
+ int i, j;
+ SYMBOL_SET *ss;
+ SYMBOL *s;
+ static char const procname[] = "compare_ksyms_lsmod";
+
+ if (!(ss_lsmod.used && ss_ksyms_modules))
+ return;
+
+ s = ss_lsmod.symbol;
+ for (i = 0; i < ss_lsmod.used; ++i, ++s) {
+ for (j = 0; j < ss_ksyms_modules; ++j) {
+ ss = ss_ksyms_module+j;
+ if (strcmp(s->name, ss->source) == 0)
+ break;
+ }
+ if (j >= ss_ksyms_modules) {
+ fprintf(stderr,
+ "Warning in %s, module %s is in lsmod but not "
+ "in ksyms, probably no symbols exported\n",
+ procname, s->name);
+ ++warnings;
+ }
+ }
+
+ for (i = 0; i < ss_ksyms_modules; ++i) {
+ ss = ss_ksyms_module+i;
+ if (!find_symbol_name(&ss_lsmod, ss->source, NULL)) {
+ fprintf(stderr,
+ "Error in %s, module %s is in ksyms but not "
+ "in lsmod\n",
+ procname, ss->source);
+ ++errors;
+ }
+ }
+}