diff options
Diffstat (limited to 'tools/perf/scripts/python')
| -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) |
