Skip to content

Instantly share code, notes, and snippets.

@ntfshard
Last active August 22, 2024 16:20
Show Gist options
  • Save ntfshard/c78167ae6b0123a44f0cdd09f9c969de to your computer and use it in GitHub Desktop.
Save ntfshard/c78167ae6b0123a44f0cdd09f9c969de to your computer and use it in GitHub Desktop.
LD_DEBUG=all to graphviz visualizer
#!/usr/bin/env python3
'''
Usage:
> LD_DEBUG=all LD_DEBUG_OUTPUT=logname ${COMMAND} ${ARGS}...
> cat logname.${PID} | parse.py | dot -Tsvg > output.svg
Or copy output of script to online visualizer, like this https://dreampuf.github.io/GraphvizOnline
(dot is from graphviz package)
Legend:
Arrows goes from from executable/library to it dependency;
If we looked for some symbols from library it will have such fields in description:
* symbols: how much symbols was exported from this library
* syms_miss: how many attempts was done to find this symbol in other libraries (too high? reorder libraries)
* file_miss: hom many path was checked to find a library location (too high? clean LD_LIBRARY_PATH)
'''
import sys
def clearName(name):
return name.split('/')[-1].split('.')[0]
def assignSplit(exp):
return exp.split('=')[1]
def wrapName(name):
return '"' + name + '"'
symbolMissCounter = {}
libraryHitCounter = {}
print("digraph Libraries {")
for line in sys.stdin:
vals = line.split()
length = len(vals)
if length == 3:
# 55046: trying file=/usr/lib/x86_64-linux-gnu/libtinyxml.so.2.6.2
if vals[1] == "trying":
lib = clearName(assignSplit(vals[2]))
rec = libraryHitCounter.get(lib, {"syms_found":0, "syms_miss":0, "library_path_miss":0})
rec["library_path_miss"] = rec["library_path_miss"] + 1
libraryHitCounter[lib] = rec
if length == 6:
# 55046: symbol=stdup; lookup in file=/usr/libcurl.so.4 [0]
if vals[2] == "lookup":
sym = assignSplit(vals[1])[0:-1] # remove `;` symbol at the end
symbolMissCounter[sym] = symbolMissCounter.get(sym, 0) + 1
if length == 7 or length == 8:
# 55046: file=libgz-sim7.so.7 [0]; needed by ./app_exe [0]
# 55046: file=/lib/libgdal.so.30 [0]; needed by /usr/libpoppler.so.118 [0] (relocation dependency)
if vals[3] == "needed":
want = clearName(assignSplit(vals[1]))
requester = clearName(vals[5])
print('{} -> {};'.format(wrapName(requester), wrapName(want)))
if length == 11 or length == 12:
# 55046: binding file /usr/libgeos.so.3.10.2 [0] to /usr/libgeos.so.3.10.2 [0]: normal symbol `strdup'
# 55046: binding file /usr/libcurl-gnutls.so.4 [0] to /usr/libc.so.6 [0]: normal symbol `getenv' [GLIBC_2.2.5]
if vals[1] == "binding":
sym = vals[10][1:-1] # remove quotes around symbol
lib = clearName(vals[6])
rec = libraryHitCounter.get(lib, {"syms_found":0, "syms_miss":0, "library_path_miss":0})
rec["syms_found"] = rec["syms_found"] + 1
rec["syms_miss"] = rec["syms_miss"] + symbolMissCounter.get(sym, 0)
libraryHitCounter[lib] = rec
print()
for lib, stat in libraryHitCounter.items():
print('{} [label="{}\\nsymbols={}\\nsyms_miss={}\\nfile_miss={}"];'.format(wrapName(lib), lib, stat["syms_found"], stat["syms_miss"], stat["library_path_miss"]))
print("}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment