diff options
Diffstat (limited to 'tools/perf/scripts/python/exported-sql-viewer.py')
-rwxr-xr-x | tools/perf/scripts/python/exported-sql-viewer.py | 547 |
1 files changed, 547 insertions, 0 deletions
diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py index ef822d850109..24cb0bd56afa 100755 --- a/tools/perf/scripts/python/exported-sql-viewer.py +++ b/tools/perf/scripts/python/exported-sql-viewer.py | |||
@@ -46,6 +46,48 @@ | |||
46 | # 'Branch Count' is the total number of branches for that function and all | 46 | # 'Branch Count' is the total number of branches for that function and all |
47 | # functions that it calls | 47 | # functions that it calls |
48 | 48 | ||
49 | # There is also a "All branches" report, which displays branches and | ||
50 | # possibly disassembly. However, presently, the only supported disassembler is | ||
51 | # Intel XED, and additionally the object code must be present in perf build ID | ||
52 | # cache. To use Intel XED, libxed.so must be present. To build and install | ||
53 | # libxed.so: | ||
54 | # git clone https://github.com/intelxed/mbuild.git mbuild | ||
55 | # git clone https://github.com/intelxed/xed | ||
56 | # cd xed | ||
57 | # ./mfile.py --share | ||
58 | # sudo ./mfile.py --prefix=/usr/local install | ||
59 | # sudo ldconfig | ||
60 | # | ||
61 | # Example report: | ||
62 | # | ||
63 | # Time CPU Command PID TID Branch Type In Tx Branch | ||
64 | # 8107675239590 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so) | ||
65 | # 7fab593ea260 48 89 e7 mov %rsp, %rdi | ||
66 | # 8107675239899 2 ls 22011 22011 hardware interrupt No 7fab593ea260 _start (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) | ||
67 | # 8107675241900 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea260 _start (ld-2.19.so) | ||
68 | # 7fab593ea260 48 89 e7 mov %rsp, %rdi | ||
69 | # 7fab593ea263 e8 c8 06 00 00 callq 0x7fab593ea930 | ||
70 | # 8107675241900 2 ls 22011 22011 call No 7fab593ea263 _start+0x3 (ld-2.19.so) -> 7fab593ea930 _dl_start (ld-2.19.so) | ||
71 | # 7fab593ea930 55 pushq %rbp | ||
72 | # 7fab593ea931 48 89 e5 mov %rsp, %rbp | ||
73 | # 7fab593ea934 41 57 pushq %r15 | ||
74 | # 7fab593ea936 41 56 pushq %r14 | ||
75 | # 7fab593ea938 41 55 pushq %r13 | ||
76 | # 7fab593ea93a 41 54 pushq %r12 | ||
77 | # 7fab593ea93c 53 pushq %rbx | ||
78 | # 7fab593ea93d 48 89 fb mov %rdi, %rbx | ||
79 | # 7fab593ea940 48 83 ec 68 sub $0x68, %rsp | ||
80 | # 7fab593ea944 0f 31 rdtsc | ||
81 | # 7fab593ea946 48 c1 e2 20 shl $0x20, %rdx | ||
82 | # 7fab593ea94a 89 c0 mov %eax, %eax | ||
83 | # 7fab593ea94c 48 09 c2 or %rax, %rdx | ||
84 | # 7fab593ea94f 48 8b 05 1a 15 22 00 movq 0x22151a(%rip), %rax | ||
85 | # 8107675242232 2 ls 22011 22011 hardware interrupt No 7fab593ea94f _dl_start+0x1f (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) | ||
86 | # 8107675242900 2 ls 22011 22011 return from interrupt No ffffffff86a00a67 native_irq_return_iret ([kernel]) -> 7fab593ea94f _dl_start+0x1f (ld-2.19.so) | ||
87 | # 7fab593ea94f 48 8b 05 1a 15 22 00 movq 0x22151a(%rip), %rax | ||
88 | # 7fab593ea956 48 89 15 3b 13 22 00 movq %rdx, 0x22133b(%rip) | ||
89 | # 8107675243232 2 ls 22011 22011 hardware interrupt No 7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) | ||
90 | |||
49 | import sys | 91 | import sys |
50 | import weakref | 92 | import weakref |
51 | import threading | 93 | import threading |
@@ -62,6 +104,16 @@ from multiprocessing import Process, Array, Value, Event | |||
62 | 104 | ||
63 | # Data formatting helpers | 105 | # Data formatting helpers |
64 | 106 | ||
107 | def tohex(ip): | ||
108 | if ip < 0: | ||
109 | ip += 1 << 64 | ||
110 | return "%x" % ip | ||
111 | |||
112 | def offstr(offset): | ||
113 | if offset: | ||
114 | return "+0x%x" % offset | ||
115 | return "" | ||
116 | |||
65 | def dsoname(name): | 117 | def dsoname(name): |
66 | if name == "[kernel.kallsyms]": | 118 | if name == "[kernel.kallsyms]": |
67 | return "[kernel]" | 119 | return "[kernel]" |
@@ -1077,6 +1129,351 @@ class FetchMoreRecordsBar(): | |||
1077 | self.in_progress = True | 1129 | self.in_progress = True |
1078 | self.start = self.model.FetchMoreRecords(self.Target()) | 1130 | self.start = self.model.FetchMoreRecords(self.Target()) |
1079 | 1131 | ||
1132 | # Brance data model level two item | ||
1133 | |||
1134 | class BranchLevelTwoItem(): | ||
1135 | |||
1136 | def __init__(self, row, text, parent_item): | ||
1137 | self.row = row | ||
1138 | self.parent_item = parent_item | ||
1139 | self.data = [""] * 8 | ||
1140 | self.data[7] = text | ||
1141 | self.level = 2 | ||
1142 | |||
1143 | def getParentItem(self): | ||
1144 | return self.parent_item | ||
1145 | |||
1146 | def getRow(self): | ||
1147 | return self.row | ||
1148 | |||
1149 | def childCount(self): | ||
1150 | return 0 | ||
1151 | |||
1152 | def hasChildren(self): | ||
1153 | return False | ||
1154 | |||
1155 | def getData(self, column): | ||
1156 | return self.data[column] | ||
1157 | |||
1158 | # Brance data model level one item | ||
1159 | |||
1160 | class BranchLevelOneItem(): | ||
1161 | |||
1162 | def __init__(self, glb, row, data, parent_item): | ||
1163 | self.glb = glb | ||
1164 | self.row = row | ||
1165 | self.parent_item = parent_item | ||
1166 | self.child_count = 0 | ||
1167 | self.child_items = [] | ||
1168 | self.data = data[1:] | ||
1169 | self.dbid = data[0] | ||
1170 | self.level = 1 | ||
1171 | self.query_done = False | ||
1172 | |||
1173 | def getChildItem(self, row): | ||
1174 | return self.child_items[row] | ||
1175 | |||
1176 | def getParentItem(self): | ||
1177 | return self.parent_item | ||
1178 | |||
1179 | def getRow(self): | ||
1180 | return self.row | ||
1181 | |||
1182 | def Select(self): | ||
1183 | self.query_done = True | ||
1184 | |||
1185 | if not self.glb.have_disassembler: | ||
1186 | return | ||
1187 | |||
1188 | query = QSqlQuery(self.glb.db) | ||
1189 | |||
1190 | QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" | ||
1191 | " FROM samples" | ||
1192 | " INNER JOIN dsos ON samples.to_dso_id = dsos.id" | ||
1193 | " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" | ||
1194 | " WHERE samples.id = " + str(self.dbid)) | ||
1195 | if not query.next(): | ||
1196 | return | ||
1197 | cpu = query.value(0) | ||
1198 | dso = query.value(1) | ||
1199 | sym = query.value(2) | ||
1200 | if dso == 0 or sym == 0: | ||
1201 | return | ||
1202 | off = query.value(3) | ||
1203 | short_name = query.value(4) | ||
1204 | long_name = query.value(5) | ||
1205 | build_id = query.value(6) | ||
1206 | sym_start = query.value(7) | ||
1207 | ip = query.value(8) | ||
1208 | |||
1209 | QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" | ||
1210 | " FROM samples" | ||
1211 | " INNER JOIN symbols ON samples.symbol_id = symbols.id" | ||
1212 | " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + | ||
1213 | " ORDER BY samples.id" | ||
1214 | " LIMIT 1") | ||
1215 | if not query.next(): | ||
1216 | return | ||
1217 | if query.value(0) != dso: | ||
1218 | # Cannot disassemble from one dso to another | ||
1219 | return | ||
1220 | bsym = query.value(1) | ||
1221 | boff = query.value(2) | ||
1222 | bsym_start = query.value(3) | ||
1223 | if bsym == 0: | ||
1224 | return | ||
1225 | tot = bsym_start + boff + 1 - sym_start - off | ||
1226 | if tot <= 0 or tot > 16384: | ||
1227 | return | ||
1228 | |||
1229 | inst = self.glb.disassembler.Instruction() | ||
1230 | f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) | ||
1231 | if not f: | ||
1232 | return | ||
1233 | mode = 0 if Is64Bit(f) else 1 | ||
1234 | self.glb.disassembler.SetMode(inst, mode) | ||
1235 | |||
1236 | buf_sz = tot + 16 | ||
1237 | buf = create_string_buffer(tot + 16) | ||
1238 | f.seek(sym_start + off) | ||
1239 | buf.value = f.read(buf_sz) | ||
1240 | buf_ptr = addressof(buf) | ||
1241 | i = 0 | ||
1242 | while tot > 0: | ||
1243 | cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) | ||
1244 | if cnt: | ||
1245 | byte_str = tohex(ip).rjust(16) | ||
1246 | for k in xrange(cnt): | ||
1247 | byte_str += " %02x" % ord(buf[i]) | ||
1248 | i += 1 | ||
1249 | while k < 15: | ||
1250 | byte_str += " " | ||
1251 | k += 1 | ||
1252 | self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self)) | ||
1253 | self.child_count += 1 | ||
1254 | else: | ||
1255 | return | ||
1256 | buf_ptr += cnt | ||
1257 | tot -= cnt | ||
1258 | buf_sz -= cnt | ||
1259 | ip += cnt | ||
1260 | |||
1261 | def childCount(self): | ||
1262 | if not self.query_done: | ||
1263 | self.Select() | ||
1264 | if not self.child_count: | ||
1265 | return -1 | ||
1266 | return self.child_count | ||
1267 | |||
1268 | def hasChildren(self): | ||
1269 | if not self.query_done: | ||
1270 | return True | ||
1271 | return self.child_count > 0 | ||
1272 | |||
1273 | def getData(self, column): | ||
1274 | return self.data[column] | ||
1275 | |||
1276 | # Brance data model root item | ||
1277 | |||
1278 | class BranchRootItem(): | ||
1279 | |||
1280 | def __init__(self): | ||
1281 | self.child_count = 0 | ||
1282 | self.child_items = [] | ||
1283 | self.level = 0 | ||
1284 | |||
1285 | def getChildItem(self, row): | ||
1286 | return self.child_items[row] | ||
1287 | |||
1288 | def getParentItem(self): | ||
1289 | return None | ||
1290 | |||
1291 | def getRow(self): | ||
1292 | return 0 | ||
1293 | |||
1294 | def childCount(self): | ||
1295 | return self.child_count | ||
1296 | |||
1297 | def hasChildren(self): | ||
1298 | return self.child_count > 0 | ||
1299 | |||
1300 | def getData(self, column): | ||
1301 | return "" | ||
1302 | |||
1303 | # Branch data preparation | ||
1304 | |||
1305 | def BranchDataPrep(query): | ||
1306 | data = [] | ||
1307 | for i in xrange(0, 8): | ||
1308 | data.append(query.value(i)) | ||
1309 | data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + | ||
1310 | " (" + dsoname(query.value(11)) + ")" + " -> " + | ||
1311 | tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + | ||
1312 | " (" + dsoname(query.value(15)) + ")") | ||
1313 | return data | ||
1314 | |||
1315 | # Branch data model | ||
1316 | |||
1317 | class BranchModel(TreeModel): | ||
1318 | |||
1319 | progress = Signal(object) | ||
1320 | |||
1321 | def __init__(self, glb, event_id, where_clause, parent=None): | ||
1322 | super(BranchModel, self).__init__(BranchRootItem(), parent) | ||
1323 | self.glb = glb | ||
1324 | self.event_id = event_id | ||
1325 | self.more = True | ||
1326 | self.populated = 0 | ||
1327 | sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," | ||
1328 | " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," | ||
1329 | " ip, symbols.name, sym_offset, dsos.short_name," | ||
1330 | " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" | ||
1331 | " FROM samples" | ||
1332 | " INNER JOIN comms ON comm_id = comms.id" | ||
1333 | " INNER JOIN threads ON thread_id = threads.id" | ||
1334 | " INNER JOIN branch_types ON branch_type = branch_types.id" | ||
1335 | " INNER JOIN symbols ON symbol_id = symbols.id" | ||
1336 | " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" | ||
1337 | " INNER JOIN dsos ON samples.dso_id = dsos.id" | ||
1338 | " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" | ||
1339 | " WHERE samples.id > $$last_id$$" + where_clause + | ||
1340 | " AND evsel_id = " + str(self.event_id) + | ||
1341 | " ORDER BY samples.id" | ||
1342 | " LIMIT " + str(glb_chunk_sz)) | ||
1343 | self.fetcher = SQLFetcher(glb, sql, BranchDataPrep, self.AddSample) | ||
1344 | self.fetcher.done.connect(self.Update) | ||
1345 | self.fetcher.Fetch(glb_chunk_sz) | ||
1346 | |||
1347 | def columnCount(self, parent=None): | ||
1348 | return 8 | ||
1349 | |||
1350 | def columnHeader(self, column): | ||
1351 | return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] | ||
1352 | |||
1353 | def columnFont(self, column): | ||
1354 | if column != 7: | ||
1355 | return None | ||
1356 | return QFont("Monospace") | ||
1357 | |||
1358 | def DisplayData(self, item, index): | ||
1359 | if item.level == 1: | ||
1360 | self.FetchIfNeeded(item.row) | ||
1361 | return item.getData(index.column()) | ||
1362 | |||
1363 | def AddSample(self, data): | ||
1364 | child = BranchLevelOneItem(self.glb, self.populated, data, self.root) | ||
1365 | self.root.child_items.append(child) | ||
1366 | self.populated += 1 | ||
1367 | |||
1368 | def Update(self, fetched): | ||
1369 | if not fetched: | ||
1370 | self.more = False | ||
1371 | self.progress.emit(0) | ||
1372 | child_count = self.root.child_count | ||
1373 | count = self.populated - child_count | ||
1374 | if count > 0: | ||
1375 | parent = QModelIndex() | ||
1376 | self.beginInsertRows(parent, child_count, child_count + count - 1) | ||
1377 | self.insertRows(child_count, count, parent) | ||
1378 | self.root.child_count += count | ||
1379 | self.endInsertRows() | ||
1380 | self.progress.emit(self.root.child_count) | ||
1381 | |||
1382 | def FetchMoreRecords(self, count): | ||
1383 | current = self.root.child_count | ||
1384 | if self.more: | ||
1385 | self.fetcher.Fetch(count) | ||
1386 | else: | ||
1387 | self.progress.emit(0) | ||
1388 | return current | ||
1389 | |||
1390 | def HasMoreRecords(self): | ||
1391 | return self.more | ||
1392 | |||
1393 | # Branch window | ||
1394 | |||
1395 | class BranchWindow(QMdiSubWindow): | ||
1396 | |||
1397 | def __init__(self, glb, event_id, name, where_clause, parent=None): | ||
1398 | super(BranchWindow, self).__init__(parent) | ||
1399 | |||
1400 | model_name = "Branch Events " + str(event_id) | ||
1401 | if len(where_clause): | ||
1402 | model_name = where_clause + " " + model_name | ||
1403 | |||
1404 | self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, where_clause)) | ||
1405 | |||
1406 | self.view = QTreeView() | ||
1407 | self.view.setUniformRowHeights(True) | ||
1408 | self.view.setModel(self.model) | ||
1409 | |||
1410 | self.ResizeColumnsToContents() | ||
1411 | |||
1412 | self.find_bar = FindBar(self, self, True) | ||
1413 | |||
1414 | self.finder = ChildDataItemFinder(self.model.root) | ||
1415 | |||
1416 | self.fetch_bar = FetchMoreRecordsBar(self.model, self) | ||
1417 | |||
1418 | self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) | ||
1419 | |||
1420 | self.setWidget(self.vbox.Widget()) | ||
1421 | |||
1422 | AddSubWindow(glb.mainwindow.mdi_area, self, name + " Branch Events") | ||
1423 | |||
1424 | def ResizeColumnToContents(self, column, n): | ||
1425 | # Using the view's resizeColumnToContents() here is extrememly slow | ||
1426 | # so implement a crude alternative | ||
1427 | mm = "MM" if column else "MMMM" | ||
1428 | font = self.view.font() | ||
1429 | metrics = QFontMetrics(font) | ||
1430 | max = 0 | ||
1431 | for row in xrange(n): | ||
1432 | val = self.model.root.child_items[row].data[column] | ||
1433 | len = metrics.width(str(val) + mm) | ||
1434 | max = len if len > max else max | ||
1435 | val = self.model.columnHeader(column) | ||
1436 | len = metrics.width(str(val) + mm) | ||
1437 | max = len if len > max else max | ||
1438 | self.view.setColumnWidth(column, max) | ||
1439 | |||
1440 | def ResizeColumnsToContents(self): | ||
1441 | n = min(self.model.root.child_count, 100) | ||
1442 | if n < 1: | ||
1443 | # No data yet, so connect a signal to notify when there is | ||
1444 | self.model.rowsInserted.connect(self.UpdateColumnWidths) | ||
1445 | return | ||
1446 | columns = self.model.columnCount() | ||
1447 | for i in xrange(columns): | ||
1448 | self.ResizeColumnToContents(i, n) | ||
1449 | |||
1450 | def UpdateColumnWidths(self, *x): | ||
1451 | # This only needs to be done once, so disconnect the signal now | ||
1452 | self.model.rowsInserted.disconnect(self.UpdateColumnWidths) | ||
1453 | self.ResizeColumnsToContents() | ||
1454 | |||
1455 | def Find(self, value, direction, pattern, context): | ||
1456 | self.view.setFocus() | ||
1457 | self.find_bar.Busy() | ||
1458 | self.finder.Find(value, direction, pattern, context, self.FindDone) | ||
1459 | |||
1460 | def FindDone(self, row): | ||
1461 | self.find_bar.Idle() | ||
1462 | if row >= 0: | ||
1463 | self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) | ||
1464 | else: | ||
1465 | self.find_bar.NotFound() | ||
1466 | |||
1467 | # Event list | ||
1468 | |||
1469 | def GetEventList(db): | ||
1470 | events = [] | ||
1471 | query = QSqlQuery(db) | ||
1472 | QueryExec(query, "SELECT name FROM selected_events WHERE id > 0 ORDER BY id") | ||
1473 | while query.next(): | ||
1474 | events.append(query.value(0)) | ||
1475 | return events | ||
1476 | |||
1080 | # SQL data preparation | 1477 | # SQL data preparation |
1081 | 1478 | ||
1082 | def SQLTableDataPrep(query, count): | 1479 | def SQLTableDataPrep(query, count): |
@@ -1448,6 +1845,8 @@ class MainWindow(QMainWindow): | |||
1448 | reports_menu = menu.addMenu("&Reports") | 1845 | reports_menu = menu.addMenu("&Reports") |
1449 | reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) | 1846 | reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) |
1450 | 1847 | ||
1848 | self.EventMenu(GetEventList(glb.db), reports_menu) | ||
1849 | |||
1451 | self.TableMenu(GetTableList(glb), menu) | 1850 | self.TableMenu(GetTableList(glb), menu) |
1452 | 1851 | ||
1453 | self.window_menu = WindowMenu(self.mdi_area, menu) | 1852 | self.window_menu = WindowMenu(self.mdi_area, menu) |
@@ -1476,6 +1875,20 @@ class MainWindow(QMainWindow): | |||
1476 | win = self.mdi_area.activeSubWindow() | 1875 | win = self.mdi_area.activeSubWindow() |
1477 | EnlargeFont(win.view) | 1876 | EnlargeFont(win.view) |
1478 | 1877 | ||
1878 | def EventMenu(self, events, reports_menu): | ||
1879 | branches_events = 0 | ||
1880 | for event in events: | ||
1881 | event = event.split(":")[0] | ||
1882 | if event == "branches": | ||
1883 | branches_events += 1 | ||
1884 | dbid = 0 | ||
1885 | for event in events: | ||
1886 | dbid += 1 | ||
1887 | event = event.split(":")[0] | ||
1888 | if event == "branches": | ||
1889 | label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")" | ||
1890 | reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self)) | ||
1891 | |||
1479 | def TableMenu(self, tables, menu): | 1892 | def TableMenu(self, tables, menu): |
1480 | table_menu = menu.addMenu("&Tables") | 1893 | table_menu = menu.addMenu("&Tables") |
1481 | for table in tables: | 1894 | for table in tables: |
@@ -1484,9 +1897,112 @@ class MainWindow(QMainWindow): | |||
1484 | def NewCallGraph(self): | 1897 | def NewCallGraph(self): |
1485 | CallGraphWindow(self.glb, self) | 1898 | CallGraphWindow(self.glb, self) |
1486 | 1899 | ||
1900 | def NewBranchView(self, event_id): | ||
1901 | BranchWindow(self.glb, event_id, "", "", self) | ||
1902 | |||
1487 | def NewTableView(self, table_name): | 1903 | def NewTableView(self, table_name): |
1488 | TableWindow(self.glb, table_name, self) | 1904 | TableWindow(self.glb, table_name, self) |
1489 | 1905 | ||
1906 | # XED Disassembler | ||
1907 | |||
1908 | class xed_state_t(Structure): | ||
1909 | |||
1910 | _fields_ = [ | ||
1911 | ("mode", c_int), | ||
1912 | ("width", c_int) | ||
1913 | ] | ||
1914 | |||
1915 | class XEDInstruction(): | ||
1916 | |||
1917 | def __init__(self, libxed): | ||
1918 | # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion | ||
1919 | xedd_t = c_byte * 512 | ||
1920 | self.xedd = xedd_t() | ||
1921 | self.xedp = addressof(self.xedd) | ||
1922 | libxed.xed_decoded_inst_zero(self.xedp) | ||
1923 | self.state = xed_state_t() | ||
1924 | self.statep = addressof(self.state) | ||
1925 | # Buffer for disassembled instruction text | ||
1926 | self.buffer = create_string_buffer(256) | ||
1927 | self.bufferp = addressof(self.buffer) | ||
1928 | |||
1929 | class LibXED(): | ||
1930 | |||
1931 | def __init__(self): | ||
1932 | self.libxed = CDLL("libxed.so") | ||
1933 | |||
1934 | self.xed_tables_init = self.libxed.xed_tables_init | ||
1935 | self.xed_tables_init.restype = None | ||
1936 | self.xed_tables_init.argtypes = [] | ||
1937 | |||
1938 | self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero | ||
1939 | self.xed_decoded_inst_zero.restype = None | ||
1940 | self.xed_decoded_inst_zero.argtypes = [ c_void_p ] | ||
1941 | |||
1942 | self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode | ||
1943 | self.xed_operand_values_set_mode.restype = None | ||
1944 | self.xed_operand_values_set_mode.argtypes = [ c_void_p, c_void_p ] | ||
1945 | |||
1946 | self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode | ||
1947 | self.xed_decoded_inst_zero_keep_mode.restype = None | ||
1948 | self.xed_decoded_inst_zero_keep_mode.argtypes = [ c_void_p ] | ||
1949 | |||
1950 | self.xed_decode = self.libxed.xed_decode | ||
1951 | self.xed_decode.restype = c_int | ||
1952 | self.xed_decode.argtypes = [ c_void_p, c_void_p, c_uint ] | ||
1953 | |||
1954 | self.xed_format_context = self.libxed.xed_format_context | ||
1955 | self.xed_format_context.restype = c_uint | ||
1956 | self.xed_format_context.argtypes = [ c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p ] | ||
1957 | |||
1958 | self.xed_tables_init() | ||
1959 | |||
1960 | def Instruction(self): | ||
1961 | return XEDInstruction(self) | ||
1962 | |||
1963 | def SetMode(self, inst, mode): | ||
1964 | if mode: | ||
1965 | inst.state.mode = 4 # 32-bit | ||
1966 | inst.state.width = 4 # 4 bytes | ||
1967 | else: | ||
1968 | inst.state.mode = 1 # 64-bit | ||
1969 | inst.state.width = 8 # 8 bytes | ||
1970 | self.xed_operand_values_set_mode(inst.xedp, inst.statep) | ||
1971 | |||
1972 | def DisassembleOne(self, inst, bytes_ptr, bytes_cnt, ip): | ||
1973 | self.xed_decoded_inst_zero_keep_mode(inst.xedp) | ||
1974 | err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) | ||
1975 | if err: | ||
1976 | return 0, "" | ||
1977 | # Use AT&T mode (2), alternative is Intel (3) | ||
1978 | ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) | ||
1979 | if not ok: | ||
1980 | return 0, "" | ||
1981 | # Return instruction length and the disassembled instruction text | ||
1982 | # For now, assume the length is in byte 166 | ||
1983 | return inst.xedd[166], inst.buffer.value | ||
1984 | |||
1985 | def TryOpen(file_name): | ||
1986 | try: | ||
1987 | return open(file_name, "rb") | ||
1988 | except: | ||
1989 | return None | ||
1990 | |||
1991 | def Is64Bit(f): | ||
1992 | result = sizeof(c_void_p) | ||
1993 | # ELF support only | ||
1994 | pos = f.tell() | ||
1995 | f.seek(0) | ||
1996 | header = f.read(7) | ||
1997 | f.seek(pos) | ||
1998 | magic = header[0:4] | ||
1999 | eclass = ord(header[4]) | ||
2000 | encoding = ord(header[5]) | ||
2001 | version = ord(header[6]) | ||
2002 | if magic == chr(127) + "ELF" and eclass > 0 and eclass < 3 and encoding > 0 and encoding < 3 and version == 1: | ||
2003 | result = True if eclass == 2 else False | ||
2004 | return result | ||
2005 | |||
1490 | # Global data | 2006 | # Global data |
1491 | 2007 | ||
1492 | class Glb(): | 2008 | class Glb(): |
@@ -1495,9 +2011,40 @@ class Glb(): | |||
1495 | self.dbref = dbref | 2011 | self.dbref = dbref |
1496 | self.db = db | 2012 | self.db = db |
1497 | self.dbname = dbname | 2013 | self.dbname = dbname |
2014 | self.home_dir = os.path.expanduser("~") | ||
2015 | self.buildid_dir = os.getenv("PERF_BUILDID_DIR") | ||
2016 | if self.buildid_dir: | ||
2017 | self.buildid_dir += "/.build-id/" | ||
2018 | else: | ||
2019 | self.buildid_dir = self.home_dir + "/.debug/.build-id/" | ||
1498 | self.app = None | 2020 | self.app = None |
1499 | self.mainwindow = None | 2021 | self.mainwindow = None |
1500 | self.instances_to_shutdown_on_exit = weakref.WeakSet() | 2022 | self.instances_to_shutdown_on_exit = weakref.WeakSet() |
2023 | try: | ||
2024 | self.disassembler = LibXED() | ||
2025 | self.have_disassembler = True | ||
2026 | except: | ||
2027 | self.have_disassembler = False | ||
2028 | |||
2029 | def FileFromBuildId(self, build_id): | ||
2030 | file_name = self.buildid_dir + build_id[0:2] + "/" + build_id[2:] + "/elf" | ||
2031 | return TryOpen(file_name) | ||
2032 | |||
2033 | def FileFromNamesAndBuildId(self, short_name, long_name, build_id): | ||
2034 | # Assume current machine i.e. no support for virtualization | ||
2035 | if short_name[0:7] == "[kernel" and os.path.basename(long_name) == "kcore": | ||
2036 | file_name = os.getenv("PERF_KCORE") | ||
2037 | f = TryOpen(file_name) if file_name else None | ||
2038 | if f: | ||
2039 | return f | ||
2040 | # For now, no special handling if long_name is /proc/kcore | ||
2041 | f = TryOpen(long_name) | ||
2042 | if f: | ||
2043 | return f | ||
2044 | f = self.FileFromBuildId(build_id) | ||
2045 | if f: | ||
2046 | return f | ||
2047 | return None | ||
1501 | 2048 | ||
1502 | def AddInstanceToShutdownOnExit(self, instance): | 2049 | def AddInstanceToShutdownOnExit(self, instance): |
1503 | self.instances_to_shutdown_on_exit.add(instance) | 2050 | self.instances_to_shutdown_on_exit.add(instance) |