Home History Comparisons Examples Documentation Help File Download News Wish List Soapbox Project: Forums Tracker CVS Hosted by: Sourceforge VA Linux Author |
Hawk / Ftwalk Examples: nmqtThis script uses nm to keep a table of global functions for a set of .o files. The table is kept in a cache directory, so that nm only needs to be run if the .o file is changed. The .o files of interest are specified on the command line. The cache mechanism can be disabled by the -nocache command line option. The names are filtered to provide C++ demangling. The demangle option can be disabled with the -nofilt option. Once the updated function information is read into memory, the user is prompted. When users enter a function name, the name is looked up. If there is an exact match, the function name and the file in which it is defined are printed. If there is no exact match, any function name that has a partial match is printed. Users can also look for external function calls by entering u then the function name after a space. The input l prints all function definitions, u prints all external references, and q exits the program. Different systems have different output formats for nm. The example code works for Silicon Graphics IRIX 5 and Unixware 2 systems. The differences are handled by initialization functions, which set functors to distinguish the cases. # initialization for Silicon Graphic IRIX 5 function init_sgi5() { Cmd = "nm " EgrepFilt = "(Symbols from|\|Proc)" Filter = "/usr/lib/c++/c++filt" TestFuncs = functor (l) { l[4] == "Proc" && l[6] == "Text" } TestUFuncs = functor (l) { l[4] == "Proc" && l[6] == "Undefined" } NmI = 7 } # initialization for Unixware 2 function init_uw2() { Cmd = "nm " EgrepFilt = "(Symbols from|\|FUNC|\|NOTY)" Filter = whence("c++filt") TestFuncs = functor (l) { l[4] == "FUNC" && l[5] == "GLOB" } TestUFuncs = functor (l) { l[4] == "NOTY" && l[7] == "UNDEF" } NmI = 8 } # read cache file in. Funcs is a map from function name to file name. # UFuncs is a map from function name to list of file names. function read_cache(cf) { F = open(cf) while (Ln = F.get) { L = split(Ln, " ") FNm = L[1] Nm = L[3] switch (L[2]) { case "T": Funcs[Nm] = FNm case "U": if (Nm in UFuncs) UFuncs[Nm] += FNm else UFuncs[Nm] = list(FNm) } } F.close } # compare .o files against cache files, put any files that do not match # cache into NmList for processing. cache filenames have '_' appended, # and are timestamp synched to .o files. function check_nm() { if (NoCache) NmList = ARGV else { NmList = list if (!isdir(Cache) && mkdir(Cache)) die("Cannot create cache directory: %(Cache) [%m]") for (i in ARGV) { CacheF = path(Cache, filename(i) "_") if ((St = stat(CacheF)) && St.st_mtime == st_mtime(i)) read_cache(CacheF) else NmList += i } } } # common code to close previous cache file, open new one (if nm). function do_cache(nm) { if (G != void) { G.close utime(CacheF, St) } if (nm && (St = stat(nm))) { CacheF = path(Cache, nm "_") G = open(CacheF, "w") } } # run nm on all files in NmList, copying new information into cache. # the egrep filter cuts down on the verbiage before c++filt. this is # pretty slow any way you do it. function run_nm() { G = void for (i in NmList) { FName = filename(i) cmd = sprintf("%s %s | egrep '%s'", Cmd, i, EgrepFilt) if (Filter) cmd = cmd " | " Filter F = popen(cmd) if (!NoCache) do_cache(FName) while ((Ln = F.get) != void) { # squeeze out excess spaces and split on "|" Ln.gsub(R"[ \t]*|[ \t]*", "|") L = split(Ln, "|") switch (L.length) { case 1: if (Ln ~ R"^Symbols from ") { FName = sub(R"Symbols from ([^:]*)$0:", "$0", Ln) } case NmI: Nm = L[NmI] if (TestFuncs(L)) { Funcs[Nm] = FName if (G) G.printf("%s T %s\n", FName, Nm) } else if (TestUFuncs(L)) { if (!(Nm in UFuncs)) UFuncs[Nm] = list(FName) else UFuncs[Nm] += FName if (G) G.printf("%s T %s\n", FName, Nm) } } } F.close } do_cache() printf("Got %d functions\n", Funcs.length) printf("Got %d undefined functions\n", UFuncs.length) } # print filename for one/more/all function definitions. function list_funcs(Nm) { if (Nm && Nm in Funcs) printf("%s: %s\n", Nm, Funcs[Nm]) else { Found = 0 for (i in Funcs) { if (!Nm || Nm in i) { printf("%s: %s\n", i, Funcs[i]) Found++ } } if (!Found) print("Not found.") } } # print filename for one/more/all undefineds. function list_undefs(Nm) { if (Nm && Nm in UFuncs) printf("%s: %s\n", Nm, string(l: ", ", UFuncs[Nm])) else { Found = 0 for (i in UFuncs) { if (!Nm || Nm in i) { printf("%s: %s\n", i, string(l: ", ", UFuncs[i])) Found++ } } if (!Found) print("Not found.") } } BEGIN { Funcs = map UFuncs = map Cache = ".nmqt_cache" # initialize according to platform switch (version.name) { case "sgi5": init_sgi5 case "uw2": init_uw2 default: die("program only configured for sgi5/uw2") } if ("nofilt" in OPTIONS) Filter = void NoCache = "nocache" in OPTIONS } MAIN { check_nm if (NmList) run_nm printf("Enter function name (use 'u ...' for undefined).\n> ") while (Ln = gets) { L = split(Ln, " ") switch (L[1]) { case "l": # list all defined functions list_funcs("") case "u": # list all undefined functions list_undefs(L.length > 1 ? L[2] : "") case "q": # quit break default: # "u NAME" queries for undefined, NAME for defined list_funcs(L[1]) } printf("> ") } }This script could be extended to handle .a archive files, global variables, additional queries.
|