export-to-sqlite.py 13 KB

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