2018-12-31 15:28:51 -08:00
#!/usr/bin/env python3
from sys import stderr , exit
2019-02-09 15:17:19 -08:00
from subprocess import Popen , PIPE
2018-12-31 15:28:51 -08:00
from struct import unpack , calcsize
from enum import Enum
class symtype ( Enum ) :
LOCAL = 0
IMPORT = 1
EXPORT = 2
def unpack_file ( fmt , file ) :
size = calcsize ( fmt )
return unpack ( fmt , file . read ( size ) )
def read_string ( file ) :
buf = bytearray ( )
while True :
b = file . read ( 1 )
if b is None or b == b ' \0 ' :
return buf . decode ( )
else :
buf + = b
# Fix broken pipe when using `head`
from signal import signal , SIGPIPE , SIG_DFL
signal ( SIGPIPE , SIG_DFL )
import argparse
parser = argparse . ArgumentParser ( description = " Parse the symfile to find unnamed symbols " )
parser . add_argument ( ' symfile ' , type = argparse . FileType ( ' r ' ) , help = " the list of symbols " )
parser . add_argument ( ' -r ' , ' --rootdir ' , type = str , help = " scan the output files to obtain a list of files with unnamed symbols (NOTE: will rebuild objects as necessary) " )
args = parser . parse_args ( )
# Get list of object files
objects = None
if args . rootdir :
2019-02-09 15:17:19 -08:00
for line in Popen ( [ " make " , " -C " , args . rootdir , " -s " , " -p " ] ,
stdout = PIPE ) . stdout . read ( ) . decode ( ) . split ( " \n " ) :
2018-12-31 15:28:51 -08:00
if line . startswith ( " crystal_obj := " ) :
objects = line [ 15 : ] . strip ( ) . split ( )
break
else :
print ( " Error: Object files not found! " , file = stderr )
exit ( 1 )
# Scan all unnamed symbols from the symfile
2019-01-01 02:41:48 -08:00
symbols_total = 0
2018-12-31 15:28:51 -08:00
symbols = set ( )
for line in args . symfile :
line = line . split ( " ; " ) [ 0 ] . strip ( )
split = line . split ( " " )
if len ( split ) < 2 :
continue
2019-01-01 02:41:48 -08:00
symbols_total + = 1
2018-12-31 15:28:51 -08:00
symbol = " " . join ( split [ 1 : ] ) . strip ( )
if symbol [ - 3 : ] . lower ( ) == split [ 0 ] [ - 3 : ] . lower ( ) :
symbols . add ( symbol )
# If no object files were provided, just print what we know and exit
2019-01-01 02:41:48 -08:00
print ( " Unnamed symbols: %d ( %.2f %% complete) " % ( len ( symbols ) ,
( symbols_total - len ( symbols ) ) / symbols_total * 100 ) )
2018-12-31 15:28:51 -08:00
if not objects :
for sym in symbols :
print ( sym )
exit ( )
# Count the amount of symbols in each file
files = { }
for objfile in objects :
f = open ( objfile , " rb " )
2020-02-04 14:19:41 -08:00
obj_ver = None
magic = unpack_file ( " 4s " , f ) [ 0 ]
if magic == b ' RGB6 ' :
obj_ver = 6
elif magic == b ' RGB9 ' :
obj_ver = 10 + unpack_file ( " <I " , f ) [ 0 ]
if obj_ver not in [ 6 , 10 ] :
print ( " Error: File ' %s ' is of an unknown format. " % objfile , file = stderr )
2018-12-31 15:28:51 -08:00
exit ( 1 )
num_symbols = unpack_file ( " <II " , f ) [ 0 ]
for x in range ( num_symbols ) :
sym_name = read_string ( f )
sym_type = symtype ( unpack_file ( " <B " , f ) [ 0 ] )
if sym_type == symtype . IMPORT :
continue
sym_filename = read_string ( f )
unpack_file ( " <III " , f )
if sym_name not in symbols :
continue
if sym_filename not in files :
files [ sym_filename ] = 0
files [ sym_filename ] + = 1
# Sort the files, the one with the most amount of symbols first
files = sorted ( [ ( fname , files [ fname ] ) for fname in files ] , key = lambda x : x [ 1 ] , reverse = True )
for f in files :
print ( " %s : %d " % ( f [ 0 ] , f [ 1 ] ) )