export-to-sqlite.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. # export-to-sqlite.py: export perf data to a sqlite3 database
  2. # Copyright (c) 2017, Intel Corporation.
  3. #
  4. # This program is free software; you can redistribute it and/or modify it
  5. # under the terms and conditions of the GNU General Public License,
  6. # version 2, as published by the Free Software Foundation.
  7. #
  8. # This program is distributed in the hope it will be useful, but WITHOUT
  9. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  11. # more details.
  12. from __future__ import print_function
  13. import os
  14. import sys
  15. import struct
  16. import datetime
  17. # To use this script you will need to have installed package python-pyside which
  18. # provides LGPL-licensed Python bindings for Qt. You will also need the package
  19. # libqt4-sql-sqlite for Qt sqlite3 support.
  20. #
  21. # An example of using this script with Intel PT:
  22. #
  23. # $ perf record -e intel_pt//u ls
  24. # $ perf script -s ~/libexec/perf-core/scripts/python/export-to-sqlite.py pt_example branches calls
  25. # 2017-07-31 14:26:07.326913 Creating database...
  26. # 2017-07-31 14:26:07.538097 Writing records...
  27. # 2017-07-31 14:26:09.889292 Adding indexes
  28. # 2017-07-31 14:26:09.958746 Done
  29. #
  30. # To browse the database, sqlite3 can be used e.g.
  31. #
  32. # $ sqlite3 pt_example
  33. # sqlite> .header on
  34. # sqlite> select * from samples_view where id < 10;
  35. # sqlite> .mode column
  36. # sqlite> select * from samples_view where id < 10;
  37. # sqlite> .tables
  38. # sqlite> .schema samples_view
  39. # sqlite> .quit
  40. #
  41. # An example of using the database is provided by the script
  42. # call-graph-from-sql.py. Refer to that script for details.
  43. #
  44. # The database structure is practically the same as created by the script
  45. # export-to-postgresql.py. Refer to that script for details. A notable
  46. # difference is the 'transaction' column of the 'samples' table which is
  47. # renamed 'transaction_' in sqlite because 'transaction' is a reserved word.
  48. from PySide.QtSql import *
  49. sys.path.append(os.environ['PERF_EXEC_PATH'] + \
  50. '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
  51. # These perf imports are not used at present
  52. #from perf_trace_context import *
  53. #from Core import *
  54. perf_db_export_mode = True
  55. perf_db_export_calls = False
  56. perf_db_export_callchains = False
  57. def printerr(*args, **keyword_args):
  58. print(*args, file=sys.stderr, **keyword_args)
  59. def usage():
  60. printerr("Usage is: export-to-sqlite.py <database name> [<columns>] [<calls>] [<callchains>]");
  61. printerr("where: columns 'all' or 'branches'");
  62. printerr(" calls 'calls' => create calls and call_paths table");
  63. printerr(" callchains 'callchains' => create call_paths table");
  64. raise Exception("Too few arguments")
  65. if (len(sys.argv) < 2):
  66. usage()
  67. dbname = sys.argv[1]
  68. if (len(sys.argv) >= 3):
  69. columns = sys.argv[2]
  70. else:
  71. columns = "all"
  72. if columns not in ("all", "branches"):
  73. usage()
  74. branches = (columns == "branches")
  75. for i in range(3,len(sys.argv)):
  76. if (sys.argv[i] == "calls"):
  77. perf_db_export_calls = True
  78. elif (sys.argv[i] == "callchains"):
  79. perf_db_export_callchains = True
  80. else:
  81. usage()
  82. def do_query(q, s):
  83. if (q.exec_(s)):
  84. return
  85. raise Exception("Query failed: " + q.lastError().text())
  86. def do_query_(q):
  87. if (q.exec_()):
  88. return
  89. raise Exception("Query failed: " + q.lastError().text())
  90. print(datetime.datetime.today(), "Creating database ...")
  91. db_exists = False
  92. try:
  93. f = open(dbname)
  94. f.close()
  95. db_exists = True
  96. except:
  97. pass
  98. if db_exists:
  99. raise Exception(dbname + " already exists")
  100. db = QSqlDatabase.addDatabase('QSQLITE')
  101. db.setDatabaseName(dbname)
  102. db.open()
  103. query = QSqlQuery(db)
  104. do_query(query, 'PRAGMA journal_mode = OFF')
  105. do_query(query, 'BEGIN TRANSACTION')
  106. do_query(query, 'CREATE TABLE selected_events ('
  107. 'id integer NOT NULL PRIMARY KEY,'
  108. 'name varchar(80))')
  109. do_query(query, 'CREATE TABLE machines ('
  110. 'id integer NOT NULL PRIMARY KEY,'
  111. 'pid integer,'
  112. 'root_dir varchar(4096))')
  113. do_query(query, 'CREATE TABLE threads ('
  114. 'id integer NOT NULL PRIMARY KEY,'
  115. 'machine_id bigint,'
  116. 'process_id bigint,'
  117. 'pid integer,'
  118. 'tid integer)')
  119. do_query(query, 'CREATE TABLE comms ('
  120. 'id integer NOT NULL PRIMARY KEY,'
  121. 'comm varchar(16))')
  122. do_query(query, 'CREATE TABLE comm_threads ('
  123. 'id integer NOT NULL PRIMARY KEY,'
  124. 'comm_id bigint,'
  125. 'thread_id bigint)')
  126. do_query(query, 'CREATE TABLE dsos ('
  127. 'id integer NOT NULL PRIMARY KEY,'
  128. 'machine_id bigint,'
  129. 'short_name varchar(256),'
  130. 'long_name varchar(4096),'
  131. 'build_id varchar(64))')
  132. do_query(query, 'CREATE TABLE symbols ('
  133. 'id integer NOT NULL PRIMARY KEY,'
  134. 'dso_id bigint,'
  135. 'sym_start bigint,'
  136. 'sym_end bigint,'
  137. 'binding integer,'
  138. 'name varchar(2048))')
  139. do_query(query, 'CREATE TABLE branch_types ('
  140. 'id integer NOT NULL PRIMARY KEY,'
  141. 'name varchar(80))')
  142. if branches:
  143. do_query(query, 'CREATE TABLE samples ('
  144. 'id integer NOT NULL PRIMARY KEY,'
  145. 'evsel_id bigint,'
  146. 'machine_id bigint,'
  147. 'thread_id bigint,'
  148. 'comm_id bigint,'
  149. 'dso_id bigint,'
  150. 'symbol_id bigint,'
  151. 'sym_offset bigint,'
  152. 'ip bigint,'
  153. 'time bigint,'
  154. 'cpu integer,'
  155. 'to_dso_id bigint,'
  156. 'to_symbol_id bigint,'
  157. 'to_sym_offset bigint,'
  158. 'to_ip bigint,'
  159. 'branch_type integer,'
  160. 'in_tx boolean,'
  161. 'call_path_id bigint)')
  162. else:
  163. do_query(query, 'CREATE TABLE samples ('
  164. 'id integer NOT NULL PRIMARY KEY,'
  165. 'evsel_id bigint,'
  166. 'machine_id bigint,'
  167. 'thread_id bigint,'
  168. 'comm_id bigint,'
  169. 'dso_id bigint,'
  170. 'symbol_id bigint,'
  171. 'sym_offset bigint,'
  172. 'ip bigint,'
  173. 'time bigint,'
  174. 'cpu integer,'
  175. 'to_dso_id bigint,'
  176. 'to_symbol_id bigint,'
  177. 'to_sym_offset bigint,'
  178. 'to_ip bigint,'
  179. 'period bigint,'
  180. 'weight bigint,'
  181. 'transaction_ bigint,'
  182. 'data_src bigint,'
  183. 'branch_type integer,'
  184. 'in_tx boolean,'
  185. 'call_path_id bigint)')
  186. if perf_db_export_calls or perf_db_export_callchains:
  187. do_query(query, 'CREATE TABLE call_paths ('
  188. 'id integer NOT NULL PRIMARY KEY,'
  189. 'parent_id bigint,'
  190. 'symbol_id bigint,'
  191. 'ip bigint)')
  192. if perf_db_export_calls:
  193. do_query(query, 'CREATE TABLE calls ('
  194. 'id integer NOT NULL PRIMARY KEY,'
  195. 'thread_id bigint,'
  196. 'comm_id bigint,'
  197. 'call_path_id bigint,'
  198. 'call_time bigint,'
  199. 'return_time bigint,'
  200. 'branch_count bigint,'
  201. 'call_id bigint,'
  202. 'return_id bigint,'
  203. 'parent_call_path_id bigint,'
  204. 'flags integer)')
  205. # printf was added to sqlite in version 3.8.3
  206. sqlite_has_printf = False
  207. try:
  208. do_query(query, 'SELECT printf("") FROM machines')
  209. sqlite_has_printf = True
  210. except:
  211. pass
  212. def emit_to_hex(x):
  213. if sqlite_has_printf:
  214. return 'printf("%x", ' + x + ')'
  215. else:
  216. return x
  217. do_query(query, 'CREATE VIEW machines_view AS '
  218. 'SELECT '
  219. 'id,'
  220. 'pid,'
  221. 'root_dir,'
  222. 'CASE WHEN id=0 THEN \'unknown\' WHEN pid=-1 THEN \'host\' ELSE \'guest\' END AS host_or_guest'
  223. ' FROM machines')
  224. do_query(query, 'CREATE VIEW dsos_view AS '
  225. 'SELECT '
  226. 'id,'
  227. 'machine_id,'
  228. '(SELECT host_or_guest FROM machines_view WHERE id = machine_id) AS host_or_guest,'
  229. 'short_name,'
  230. 'long_name,'
  231. 'build_id'
  232. ' FROM dsos')
  233. do_query(query, 'CREATE VIEW symbols_view AS '
  234. 'SELECT '
  235. 'id,'
  236. 'name,'
  237. '(SELECT short_name FROM dsos WHERE id=dso_id) AS dso,'
  238. 'dso_id,'
  239. 'sym_start,'
  240. 'sym_end,'
  241. 'CASE WHEN binding=0 THEN \'local\' WHEN binding=1 THEN \'global\' ELSE \'weak\' END AS binding'
  242. ' FROM symbols')
  243. do_query(query, 'CREATE VIEW threads_view AS '
  244. 'SELECT '
  245. 'id,'
  246. 'machine_id,'
  247. '(SELECT host_or_guest FROM machines_view WHERE id = machine_id) AS host_or_guest,'
  248. 'process_id,'
  249. 'pid,'
  250. 'tid'
  251. ' FROM threads')
  252. do_query(query, 'CREATE VIEW comm_threads_view AS '
  253. 'SELECT '
  254. 'comm_id,'
  255. '(SELECT comm FROM comms WHERE id = comm_id) AS command,'
  256. 'thread_id,'
  257. '(SELECT pid FROM threads WHERE id = thread_id) AS pid,'
  258. '(SELECT tid FROM threads WHERE id = thread_id) AS tid'
  259. ' FROM comm_threads')
  260. if perf_db_export_calls or perf_db_export_callchains:
  261. do_query(query, 'CREATE VIEW call_paths_view AS '
  262. 'SELECT '
  263. 'c.id,'
  264. + emit_to_hex('c.ip') + ' AS ip,'
  265. 'c.symbol_id,'
  266. '(SELECT name FROM symbols WHERE id = c.symbol_id) AS symbol,'
  267. '(SELECT dso_id FROM symbols WHERE id = c.symbol_id) AS dso_id,'
  268. '(SELECT dso FROM symbols_view WHERE id = c.symbol_id) AS dso_short_name,'
  269. 'c.parent_id,'
  270. + emit_to_hex('p.ip') + ' AS parent_ip,'
  271. 'p.symbol_id AS parent_symbol_id,'
  272. '(SELECT name FROM symbols WHERE id = p.symbol_id) AS parent_symbol,'
  273. '(SELECT dso_id FROM symbols WHERE id = p.symbol_id) AS parent_dso_id,'
  274. '(SELECT dso FROM symbols_view WHERE id = p.symbol_id) AS parent_dso_short_name'
  275. ' FROM call_paths c INNER JOIN call_paths p ON p.id = c.parent_id')
  276. if perf_db_export_calls:
  277. do_query(query, 'CREATE VIEW calls_view AS '
  278. 'SELECT '
  279. 'calls.id,'
  280. 'thread_id,'
  281. '(SELECT pid FROM threads WHERE id = thread_id) AS pid,'
  282. '(SELECT tid FROM threads WHERE id = thread_id) AS tid,'
  283. '(SELECT comm FROM comms WHERE id = comm_id) AS command,'
  284. 'call_path_id,'
  285. + emit_to_hex('ip') + ' AS ip,'
  286. 'symbol_id,'
  287. '(SELECT name FROM symbols WHERE id = symbol_id) AS symbol,'
  288. 'call_time,'
  289. 'return_time,'
  290. 'return_time - call_time AS elapsed_time,'
  291. 'branch_count,'
  292. 'call_id,'
  293. 'return_id,'
  294. 'CASE WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' ELSE \'\' END AS flags,'
  295. 'parent_call_path_id'
  296. ' FROM calls INNER JOIN call_paths ON call_paths.id = call_path_id')
  297. do_query(query, 'CREATE VIEW samples_view AS '
  298. 'SELECT '
  299. 'id,'
  300. 'time,'
  301. 'cpu,'
  302. '(SELECT pid FROM threads WHERE id = thread_id) AS pid,'
  303. '(SELECT tid FROM threads WHERE id = thread_id) AS tid,'
  304. '(SELECT comm FROM comms WHERE id = comm_id) AS command,'
  305. '(SELECT name FROM selected_events WHERE id = evsel_id) AS event,'
  306. + emit_to_hex('ip') + ' AS ip_hex,'
  307. '(SELECT name FROM symbols WHERE id = symbol_id) AS symbol,'
  308. 'sym_offset,'
  309. '(SELECT short_name FROM dsos WHERE id = dso_id) AS dso_short_name,'
  310. + emit_to_hex('to_ip') + ' AS to_ip_hex,'
  311. '(SELECT name FROM symbols WHERE id = to_symbol_id) AS to_symbol,'
  312. 'to_sym_offset,'
  313. '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,'
  314. '(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,'
  315. 'in_tx'
  316. ' FROM samples')
  317. do_query(query, 'END TRANSACTION')
  318. evsel_query = QSqlQuery(db)
  319. evsel_query.prepare("INSERT INTO selected_events VALUES (?, ?)")
  320. machine_query = QSqlQuery(db)
  321. machine_query.prepare("INSERT INTO machines VALUES (?, ?, ?)")
  322. thread_query = QSqlQuery(db)
  323. thread_query.prepare("INSERT INTO threads VALUES (?, ?, ?, ?, ?)")
  324. comm_query = QSqlQuery(db)
  325. comm_query.prepare("INSERT INTO comms VALUES (?, ?)")
  326. comm_thread_query = QSqlQuery(db)
  327. comm_thread_query.prepare("INSERT INTO comm_threads VALUES (?, ?, ?)")
  328. dso_query = QSqlQuery(db)
  329. dso_query.prepare("INSERT INTO dsos VALUES (?, ?, ?, ?, ?)")
  330. symbol_query = QSqlQuery(db)
  331. symbol_query.prepare("INSERT INTO symbols VALUES (?, ?, ?, ?, ?, ?)")
  332. branch_type_query = QSqlQuery(db)
  333. branch_type_query.prepare("INSERT INTO branch_types VALUES (?, ?)")
  334. sample_query = QSqlQuery(db)
  335. if branches:
  336. sample_query.prepare("INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
  337. else:
  338. sample_query.prepare("INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
  339. if perf_db_export_calls or perf_db_export_callchains:
  340. call_path_query = QSqlQuery(db)
  341. call_path_query.prepare("INSERT INTO call_paths VALUES (?, ?, ?, ?)")
  342. if perf_db_export_calls:
  343. call_query = QSqlQuery(db)
  344. call_query.prepare("INSERT INTO calls VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
  345. def trace_begin():
  346. print(datetime.datetime.today(), "Writing records...")
  347. do_query(query, 'BEGIN TRANSACTION')
  348. # id == 0 means unknown. It is easier to create records for them than replace the zeroes with NULLs
  349. evsel_table(0, "unknown")
  350. machine_table(0, 0, "unknown")
  351. thread_table(0, 0, 0, -1, -1)
  352. comm_table(0, "unknown")
  353. dso_table(0, 0, "unknown", "unknown", "")
  354. symbol_table(0, 0, 0, 0, 0, "unknown")
  355. sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
  356. if perf_db_export_calls or perf_db_export_callchains:
  357. call_path_table(0, 0, 0, 0)
  358. unhandled_count = 0
  359. def trace_end():
  360. do_query(query, 'END TRANSACTION')
  361. print(datetime.datetime.today(), "Adding indexes")
  362. if perf_db_export_calls:
  363. do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)')
  364. if (unhandled_count):
  365. print(datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events")
  366. print(datetime.datetime.today(), "Done")
  367. def trace_unhandled(event_name, context, event_fields_dict):
  368. global unhandled_count
  369. unhandled_count += 1
  370. def sched__sched_switch(*x):
  371. pass
  372. def bind_exec(q, n, x):
  373. for xx in x[0:n]:
  374. q.addBindValue(str(xx))
  375. do_query_(q)
  376. def evsel_table(*x):
  377. bind_exec(evsel_query, 2, x)
  378. def machine_table(*x):
  379. bind_exec(machine_query, 3, x)
  380. def thread_table(*x):
  381. bind_exec(thread_query, 5, x)
  382. def comm_table(*x):
  383. bind_exec(comm_query, 2, x)
  384. def comm_thread_table(*x):
  385. bind_exec(comm_thread_query, 3, x)
  386. def dso_table(*x):
  387. bind_exec(dso_query, 5, x)
  388. def symbol_table(*x):
  389. bind_exec(symbol_query, 6, x)
  390. def branch_type_table(*x):
  391. bind_exec(branch_type_query, 2, x)
  392. def sample_table(*x):
  393. if branches:
  394. for xx in x[0:15]:
  395. sample_query.addBindValue(str(xx))
  396. for xx in x[19:22]:
  397. sample_query.addBindValue(str(xx))
  398. do_query_(sample_query)
  399. else:
  400. bind_exec(sample_query, 22, x)
  401. def call_path_table(*x):
  402. bind_exec(call_path_query, 4, x)
  403. def call_return_table(*x):
  404. bind_exec(call_query, 11, x)