|
|
|
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.
|