3 # Find holes in structures, so that we can pack them and improve our memory density.
5 # In order to make this work, you need to
6 # (1) Be operating in a workspace where you have a __NON-DEBUG__ build of LibreOffice, but __WITH SYMBOLS__.
7 # (A debug build has different sizes for some things in the standard library.)
8 # (2) First run the unusedfields loplugin to generate a log file
9 # (3) Install the pahole stuff into your gdb, I used this one:
10 # https://github.com/PhilArmstrong/pahole-gdb
11 # (4) Edit the loop near the top of the script to only produce results for one of our modules.
12 # Note that this will make GDB soak up about 8G of RAM, which is why I don't do more than one module at a time
14 # ./compilerplugins/clang/pahole-all-classes.py > ./compilerplugins/clang/pahole.results
24 # search for all the class names in the file produced by the unusedfields loplugin
25 #a = subprocess.Popen("grep 'definition:' workdir/loplugin.unusedfields.log | sort -u", stdout=subprocess.PIPE, shell=True)
26 a
= subprocess
.Popen("cat ../libo/n1", stdout
=subprocess
.PIPE
, shell
=True)
28 classSourceLocDict
= dict()
32 tokens
= line
.decode('utf8').strip().split("\t")
33 className
= tokens
[2].strip()
34 srcLoc
= tokens
[5].strip()
35 # ignore things like unions
36 if "anonymous" in className
: continue
38 if className
in classSet
: continue
39 # for now, just check the stuff in /sc/inc
40 if srcLoc
.startswith("a"):
41 classSourceLocDict
[srcLoc
] = className
42 classSet
.add(className
)
45 gdbProc
= subprocess
.Popen("gdb", stdin
=subprocess
.PIPE
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.STDOUT
, shell
=True)
47 stdin
= io
.TextIOWrapper(gdbProc
.stdin
, 'utf-8')
49 # make gdb load all the debugging info
50 stdin
.write("set confirm off\n")
51 for filename
in sorted(os
.listdir('instdir/program')):
52 if filename
.endswith(".so"):
53 stdin
.write("add-symbol-file instdir/program/" + filename
+ "\n")
57 # Some of the pahole commands are going to fail, and I cannot read the error stream and the input stream
58 # together because python has no way of (easily) doing a non-blocking read.
59 # So I have to write the commands out using a background thread, and then read the entire resulting
61 def write_pahole_commands():
62 for srcLoc
in sorted(classSourceLocDict
.keys()):
63 className
= classSourceLocDict
[srcLoc
]
64 stdin
.write("echo " + className
+ " " + srcLoc
+ "\n")
65 stdin
.write("pahole " + className
+ "\n")
67 stdin
.write("echo all-done\n")
69 stdin
.close() # only way to make it flush the last echo command
71 _thread
.start_new_thread( write_pahole_commands
, () )
73 # Use generator because lines often end up merged together in gdb's output, and we need
74 # to split them up, and that creates a mess in the parsing logic.
77 line
= gdbProc
.stdout
.readline().decode('utf8').strip()
78 for split
in line
.split("(gdb)"):
80 if len(split
) == 0: continue
81 if "all-done" in split
: return
84 firstLineRegex
= re
.compile("/\*\s+(\d+)\s+\*/ struct")
85 fieldLineRegex
= re
.compile("/\*\s+(\d+)\s+(\d+)\s+\*/ ")
86 holeLineRegex
= re
.compile("/\* XXX (\d+) bit hole, try to pack \*/")
87 # sometimes pahole can't determine the size of a sub-struct, and then it returns bad data
88 bogusLineRegex
= re
.compile("/\*\s+\d+\s+0\s+\*/")
91 cumulativeHoleBits
= 0
93 foundBogusLine
= False
94 # pahole doesn't report space at the end of the structure, so work it out myself
96 for line
in read_generator():
97 structLines
.append(line
)
98 firstLineMatch
= firstLineRegex
.match(line
)
100 structSize
= int(firstLineMatch
.group(1))
101 holeLineMatch
= holeLineRegex
.match(line
)
104 cumulativeHoleBits
+= int(holeLineMatch
.group(1))
105 fieldLineMatch
= fieldLineRegex
.match(line
)
107 fieldSize
= int(fieldLineMatch
.group(2))
108 sizeOfFields
= int(fieldLineMatch
.group(1)) + fieldSize
109 if bogusLineRegex
.match(line
):
110 foundBogusLine
= True
112 # Ignore very large structs, packing those is not going to help much, and
113 # re-organising them can make them much less readable.
114 if foundHole
and len(structLines
) < 12 and structSize
< 100 and not foundBogusLine
:
115 # Verify that we have enough hole-space that removing it will result in a structure
116 # that still satisfies alignment requirements, otherwise the compiler will just put empty
117 # space at the end of the struct.
118 # TODO improve detection of the required alignment for a structure
119 potentialSpace
= (cumulativeHoleBits
/ 8) + (sizeOfFields
- structSize
)
120 if potentialSpace
>= 8:
121 for line
in structLines
:
123 if (sizeOfFields
- structSize
) > 0:
124 print("hole at end of struct: " + str(sizeOfFields
- structSize
))
128 cumulativeHoleBits
= 0
130 foundBogusLine
= False