|
1 | 1 | # GDB extension for LuaJIT post-mortem analysis. |
2 | 2 | # To use, just put 'source <path-to-repo>/src/luajit-gdb.py' in gdb. |
3 | 3 |
|
| 4 | +import bisect |
4 | 5 | import re |
5 | 6 | import gdb |
6 | 7 | import sys |
| 8 | +import time |
| 9 | +from contextlib import contextmanager |
| 10 | +from datetime import timedelta |
7 | 11 |
|
8 | 12 | # make script compatible with the ancient Python {{{ |
9 | 13 |
|
@@ -1115,21 +1119,85 @@ def mem_estimate_wp(gcobj): |
1115 | 1119 | return mem_estimate[int(gcobj['gch']['gct'] - ~LJ_TSTR)](gcobj) |
1116 | 1120 |
|
1117 | 1121 |
|
| 1122 | +class SortedList: |
| 1123 | + def __init__(self, key_func, num_values): |
| 1124 | + self.__key_func = key_func |
| 1125 | + self.__num_values = num_values |
| 1126 | + self.__keys = [] |
| 1127 | + self.__values = [] |
| 1128 | + |
| 1129 | + @property |
| 1130 | + def values(self): |
| 1131 | + return self.__values |
| 1132 | + |
| 1133 | + def insert(self, gco): |
| 1134 | + key = self.__key_func(gco) |
| 1135 | + i = bisect.bisect(self.__keys, key) |
| 1136 | + if len(self.__values) < self.__num_values: |
| 1137 | + self.__keys.insert(i, key) |
| 1138 | + self.__values.insert(i, gco) |
| 1139 | + elif i > 0: |
| 1140 | + self.__keys.insert(i, key) |
| 1141 | + self.__values.insert(i, gco) |
| 1142 | + del self.__keys[0] |
| 1143 | + del self.__values[0] |
| 1144 | + |
| 1145 | + |
| 1146 | +@contextmanager |
| 1147 | +def timer(): |
| 1148 | + start_time = time.time() |
| 1149 | + try: |
| 1150 | + yield |
| 1151 | + finally: |
| 1152 | + elapsed_time = int(time.time() - start_time) |
| 1153 | + gdb.write(" - took {} seconds / {}\n".format( |
| 1154 | + elapsed_time, |
| 1155 | + str(timedelta(seconds=elapsed_time)), |
| 1156 | + )) |
| 1157 | + |
| 1158 | + |
| 1159 | +def gcobjects(gc): |
| 1160 | + obj = gcref(gc['root']) |
| 1161 | + while obj: |
| 1162 | + yield obj |
| 1163 | + obj = gcref(obj['gch']['nextgc']) |
| 1164 | + |
| 1165 | + |
1118 | 1166 | def gctop(amount): |
1119 | | - result = [] |
1120 | | - g = G(L(None)) |
1121 | | - root = g['gc']['root'] |
1122 | | - while gcref(root): |
1123 | | - gcobj = gcref(root) |
1124 | | - if len(result) < amount: |
1125 | | - result.insert(len(result), gcobj) |
1126 | | - else: |
1127 | | - objsize = mem_estimate_wp(gcobj) |
1128 | | - if objsize > mem_estimate_wp(result[len(result) - 1]): |
1129 | | - result[len(result) - 1] = gcobj |
1130 | | - result.sort(key=mem_estimate_wp, reverse=True) |
1131 | | - root = gcref(root)['gch']['nextgc'] |
1132 | | - return result |
| 1167 | + gc = G(L(None))['gc'] |
| 1168 | + |
| 1169 | + with timer(): |
| 1170 | + num_gcobjs = 0 |
| 1171 | + for gcobj in gcobjects(gc): |
| 1172 | + num_gcobjs += 1 |
| 1173 | + if not num_gcobjs % 100000: |
| 1174 | + gdb.write("\rCalculating number of objects... {}".format(num_gcobjs)) |
| 1175 | + gdb.flush() |
| 1176 | + gdb.write("\rCalculating number of objects... {}".format(num_gcobjs)) |
| 1177 | + |
| 1178 | + result = SortedList(mem_estimate_wp, amount) |
| 1179 | + |
| 1180 | + with timer(): |
| 1181 | + i = 0 |
| 1182 | + lap_start_time = time.time() |
| 1183 | + lap_start_i = i |
| 1184 | + for gcobj in gcobjects(gc): |
| 1185 | + result.insert(gcobj) |
| 1186 | + i += 1 |
| 1187 | + if not i % 10000: |
| 1188 | + # Estimation is based on the last "lap". |
| 1189 | + lap_elapsed_time = time.time() - lap_start_time |
| 1190 | + estimated_time = (int(lap_elapsed_time) * (num_gcobjs - i)) // (i - lap_start_i) |
| 1191 | + gdb.write("\rCollecting statistic... {:5.2f}% (estimated time - {})".format( |
| 1192 | + 100.0 * i / num_gcobjs, |
| 1193 | + str(timedelta(seconds=estimated_time)), |
| 1194 | + )) |
| 1195 | + gdb.flush() |
| 1196 | + lap_start_time += lap_elapsed_time |
| 1197 | + lap_start_i = i |
| 1198 | + gdb.write("\rCollecting statistic... 100%") |
| 1199 | + |
| 1200 | + return result.values |
1133 | 1201 |
|
1134 | 1202 |
|
1135 | 1203 | def dump_objects(objlist): |
|
0 commit comments