display-profiler-output 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939
  1. #!/usr/bin/env ruby
  2. # Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
  3. #
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions
  6. # are met:
  7. # 1. Redistributions of source code must retain the above copyright
  8. # notice, this list of conditions and the following disclaimer.
  9. # 2. Redistributions in binary form must reproduce the above copyright
  10. # notice, this list of conditions and the following disclaimer in the
  11. # documentation and/or other materials provided with the distribution.
  12. #
  13. # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
  14. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  15. # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  16. # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
  17. # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  18. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  19. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  20. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  21. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  22. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  23. # THE POSSIBILITY OF SUCH DAMAGE.
  24. require 'rubygems'
  25. require 'readline'
  26. begin
  27. require 'json'
  28. require 'highline'
  29. rescue LoadError
  30. $stderr.puts "Error: some required gems are not installed!"
  31. $stderr.puts
  32. $stderr.puts "Try running:"
  33. $stderr.puts
  34. $stderr.puts "sudo gem install json"
  35. $stderr.puts "sudo gem install highline"
  36. exit 1
  37. end
  38. class Bytecode
  39. attr_accessor :bytecodes, :bytecodeIndex, :opcode, :description, :topCounts, :bottomCounts, :machineInlinees, :osrExits
  40. def initialize(bytecodes, bytecodeIndex, opcode, description)
  41. @bytecodes = bytecodes
  42. @bytecodeIndex = bytecodeIndex
  43. @opcode = opcode
  44. @description = description
  45. @topCounts = [] # "source" counts
  46. @bottomCounts = {} # "machine" counts, maps compilations to counts
  47. @machineInlinees = {} # maps my compilation to a set of inlinees
  48. @osrExits = []
  49. end
  50. def shouldHaveCounts?
  51. @opcode != "op_call_put_result"
  52. end
  53. def addTopCount(count)
  54. @topCounts << count
  55. end
  56. def addBottomCountForCompilation(count, compilation)
  57. @bottomCounts[compilation] = [] unless @bottomCounts[compilation]
  58. @bottomCounts[compilation] << count
  59. end
  60. def addMachineInlinee(compilation, inlinee)
  61. @machineInlinees[compilation] = {} unless @machineInlinees[compilation]
  62. @machineInlinees[compilation][inlinee] = true
  63. end
  64. def totalTopExecutionCount
  65. sum = 0
  66. @topCounts.each {
  67. | value |
  68. sum += value.count
  69. }
  70. sum
  71. end
  72. def topExecutionCount(engine)
  73. sum = 0
  74. @topCounts.each {
  75. | value |
  76. if value.engine == engine
  77. sum += value.count
  78. end
  79. }
  80. sum
  81. end
  82. def totalBottomExecutionCount
  83. sum = 0
  84. @bottomCounts.each_value {
  85. | counts |
  86. max = 0
  87. counts.each {
  88. | value |
  89. max = [max, value.count].max
  90. }
  91. sum += max
  92. }
  93. sum
  94. end
  95. def bottomExecutionCount(engine)
  96. sum = 0
  97. @bottomCounts.each_pair {
  98. | compilation, counts |
  99. if compilation.engine == engine
  100. max = 0
  101. counts.each {
  102. | value |
  103. max = [max, value.count].max
  104. }
  105. sum += max
  106. end
  107. }
  108. sum
  109. end
  110. def totalExitCount
  111. sum = 0
  112. @osrExits.each {
  113. | exit |
  114. sum += exit.count
  115. }
  116. sum
  117. end
  118. end
  119. class Bytecodes
  120. attr_accessor :codeHash, :inferredName, :source, :instructionCount, :machineInlineSites, :compilations
  121. def initialize(json)
  122. @codeHash = json["hash"].to_s
  123. @inferredName = json["inferredName"].to_s
  124. @source = json["sourceCode"].to_s
  125. @instructionCount = json["instructionCount"].to_i
  126. @bytecode = {}
  127. json["bytecode"].each {
  128. | subJson |
  129. index = subJson["bytecodeIndex"].to_i
  130. @bytecode[index] = Bytecode.new(self, index, subJson["opcode"].to_s, subJson["description"].to_s)
  131. }
  132. @machineInlineSites = {} # maps compilation to a set of origins
  133. @compilations = []
  134. end
  135. def name(limit)
  136. if to_s.size > limit
  137. "\##{@codeHash}"
  138. else
  139. to_s
  140. end
  141. end
  142. def to_s
  143. "#{@inferredName}\##{@codeHash}"
  144. end
  145. def matches(pattern)
  146. if pattern =~ /^#/
  147. $~.post_match == @codeHash
  148. elsif pattern =~ /#/
  149. pattern == to_s
  150. else
  151. pattern == @inferredName or pattern == @codeHash
  152. end
  153. end
  154. def each
  155. @bytecode.values.sort{|a, b| a.bytecodeIndex <=> b.bytecodeIndex}.each {
  156. | value |
  157. yield value
  158. }
  159. end
  160. def bytecode(bytecodeIndex)
  161. @bytecode[bytecodeIndex]
  162. end
  163. def addMachineInlineSite(compilation, origin)
  164. @machineInlineSites[compilation] = {} unless @machineInlineSites[compilation]
  165. @machineInlineSites[compilation][origin] = true
  166. end
  167. def totalMachineInlineSites
  168. sum = 0
  169. @machineInlineSites.each_value {
  170. | set |
  171. sum += set.size
  172. }
  173. sum
  174. end
  175. def sourceMachineInlineSites
  176. set = {}
  177. @machineInlineSites.each_value {
  178. | mySet |
  179. set.merge!(mySet)
  180. }
  181. set.size
  182. end
  183. def totalMaxTopExecutionCount
  184. max = 0
  185. @bytecode.each_value {
  186. | bytecode |
  187. max = [max, bytecode.totalTopExecutionCount].max
  188. }
  189. max
  190. end
  191. def maxTopExecutionCount(engine)
  192. max = 0
  193. @bytecode.each_value {
  194. | bytecode |
  195. max = [max, bytecode.topExecutionCount(engine)].max
  196. }
  197. max
  198. end
  199. def totalMaxBottomExecutionCount
  200. max = 0
  201. @bytecode.each_value {
  202. | bytecode |
  203. max = [max, bytecode.totalBottomExecutionCount].max
  204. }
  205. max
  206. end
  207. def maxBottomExecutionCount(engine)
  208. max = 0
  209. @bytecode.each_value {
  210. | bytecode |
  211. max = [max, bytecode.bottomExecutionCount(engine)].max
  212. }
  213. max
  214. end
  215. def totalExitCount
  216. sum = 0
  217. each {
  218. | bytecode |
  219. sum += bytecode.totalExitCount
  220. }
  221. sum
  222. end
  223. end
  224. class ProfiledBytecode
  225. attr_reader :bytecodeIndex, :description
  226. def initialize(json)
  227. @bytecodeIndex = json["bytecodeIndex"].to_i
  228. @description = json["description"].to_s
  229. end
  230. end
  231. class ProfiledBytecodes
  232. attr_reader :header, :bytecodes
  233. def initialize(json)
  234. @header = json["header"]
  235. @bytecodes = $bytecodes[json["bytecodesID"].to_i]
  236. @sequence = json["bytecode"].map {
  237. | subJson |
  238. ProfiledBytecode.new(subJson)
  239. }
  240. end
  241. def each
  242. @sequence.each {
  243. | description |
  244. yield description
  245. }
  246. end
  247. end
  248. def originStackFromJSON(json)
  249. json.map {
  250. | subJson |
  251. $bytecodes[subJson["bytecodesID"].to_i].bytecode(subJson["bytecodeIndex"].to_i)
  252. }
  253. end
  254. class CompiledBytecode
  255. attr_accessor :origin, :description
  256. def initialize(json)
  257. @origin = originStackFromJSON(json["origin"])
  258. @description = json["description"].to_s
  259. end
  260. end
  261. class ExecutionCounter
  262. attr_accessor :origin, :engine, :count
  263. def initialize(origin, engine, count)
  264. @origin = origin
  265. @engine = engine
  266. @count = count
  267. end
  268. end
  269. class OSRExit
  270. attr_reader :compilation, :origin, :codeAddresses, :exitKind, :isWatchpoint, :count
  271. def initialize(compilation, origin, codeAddresses, exitKind, isWatchpoint, count)
  272. @compilation = compilation
  273. @origin = origin
  274. @codeAddresses = codeAddresses
  275. @exitKind = exitKind
  276. @isWatchpoint = isWatchpoint
  277. @count = count
  278. end
  279. def dumpForDisplay(prefix)
  280. puts(prefix + "EXIT: due to #{@exitKind}, #{@count} times")
  281. end
  282. end
  283. class Compilation
  284. attr_accessor :bytecode, :engine, :descriptions, :counters, :compilationIndex
  285. attr_accessor :osrExits, :profiledBytecodes, :numInlinedGetByIds, :numInlinedPutByIds
  286. attr_accessor :numInlinedCalls
  287. def initialize(json)
  288. @bytecode = $bytecodes[json["bytecodesID"].to_i]
  289. @bytecode.compilations << self
  290. @compilationIndex = @bytecode.compilations.size
  291. @engine = json["compilationKind"]
  292. @descriptions = json["descriptions"].map {
  293. | subJson |
  294. CompiledBytecode.new(subJson)
  295. }
  296. @descriptions.each {
  297. | description |
  298. next if description.origin.empty?
  299. description.origin[1..-1].each_with_index {
  300. | inlinee, index |
  301. description.origin[0].addMachineInlinee(self, inlinee.bytecodes)
  302. inlinee.bytecodes.addMachineInlineSite(self, description.origin[0...index])
  303. }
  304. }
  305. @counters = {}
  306. json["counters"].each {
  307. | subJson |
  308. origin = originStackFromJSON(subJson["origin"])
  309. counter = ExecutionCounter.new(origin, @engine, subJson["executionCount"].to_i)
  310. @counters[origin] = counter
  311. origin[-1].addTopCount(counter)
  312. origin[0].addBottomCountForCompilation(counter, self)
  313. }
  314. @osrExits = {}
  315. json["osrExits"].each {
  316. | subJson |
  317. osrExit = OSRExit.new(self, originStackFromJSON(subJson["origin"]),
  318. json["osrExitSites"][subJson["id"]].map {
  319. | value |
  320. value.hex
  321. }, subJson["exitKind"], subJson["isWatchpoint"],
  322. subJson["count"])
  323. osrExit.codeAddresses.each {
  324. | codeAddress |
  325. osrExits[codeAddress] = [] unless osrExits[codeAddress]
  326. osrExits[codeAddress] << osrExit
  327. }
  328. osrExit.origin[-1].osrExits << osrExit
  329. }
  330. @profiledBytecodes = []
  331. json["profiledBytecodes"].each {
  332. | subJson |
  333. @profiledBytecodes << ProfiledBytecodes.new(subJson)
  334. }
  335. @numInlinedGetByIds = json["numInlinedGetByIds"]
  336. @numInlinedPutByIds = json["numInlinedPutByIds"]
  337. @numInlinedCalls = json["numInlinedCalls"]
  338. end
  339. def counter(origin)
  340. @counters[origin]
  341. end
  342. def to_s
  343. "#{bytecode}-#{compilationIndex}-#{engine}"
  344. end
  345. end
  346. class DescriptionLine
  347. attr_reader :actualCountsString, :sourceCountsString, :disassembly, :shouldShow
  348. def initialize(actualCountsString, sourceCountsString, disassembly, shouldShow)
  349. @actualCountsString = actualCountsString
  350. @sourceCountsString = sourceCountsString
  351. @disassembly = disassembly
  352. @shouldShow = shouldShow
  353. end
  354. def codeAddress
  355. if @disassembly =~ /^\s*(0x[0-9a-fA-F]+):/
  356. $1.hex
  357. else
  358. nil
  359. end
  360. end
  361. end
  362. if ARGV.length != 1
  363. $stderr.puts "Usage: display-profiler-output <path to profiler output file>"
  364. $stderr.puts
  365. $stderr.puts "The typical usage pattern for the profiler currently looks something like:"
  366. $stderr.puts
  367. $stderr.puts "Path/To/jsc -p profile.json myprogram.js"
  368. $stderr.puts "display-profiler-output profile.json"
  369. exit 1
  370. end
  371. $json = JSON::parse(IO::read(ARGV[0]))
  372. $bytecodes = $json["bytecodes"].map {
  373. | subJson |
  374. Bytecodes.new(subJson)
  375. }
  376. $compilations = $json["compilations"].map {
  377. | subJson |
  378. Compilation.new(subJson)
  379. }
  380. $engines = ["Baseline", "DFG"]
  381. def lpad(str,chars)
  382. if str.length>chars
  383. str
  384. else
  385. "%#{chars}s"%(str)
  386. end
  387. end
  388. def rpad(str, chars)
  389. while str.length < chars
  390. str += " "
  391. end
  392. str
  393. end
  394. def center(str, chars)
  395. while str.length < chars
  396. str += " "
  397. if str.length < chars
  398. str = " " + str
  399. end
  400. end
  401. str
  402. end
  403. def mayBeHash(hash)
  404. hash =~ /#/ or hash.size == 6
  405. end
  406. def sourceOnOneLine(source, limit)
  407. source.gsub(/\s+/, ' ')[0...limit]
  408. end
  409. def screenWidth
  410. if $stdin.tty?
  411. HighLine::SystemExtensions.terminal_size[0]
  412. else
  413. 200
  414. end
  415. end
  416. def summary(mode)
  417. remaining = screenWidth
  418. # Figure out how many columns we need for the code block names, and for counts
  419. maxCount = 0
  420. maxName = 0
  421. $bytecodes.each {
  422. | bytecodes |
  423. maxCount = ([maxCount] + $engines.map {
  424. | engine |
  425. bytecodes.maxTopExecutionCount(engine)
  426. } + $engines.map {
  427. | engine |
  428. bytecodes.maxBottomExecutionCount(engine)
  429. }).max
  430. maxName = [bytecodes.to_s.size, maxName].max
  431. }
  432. maxCountDigits = maxCount.to_s.size
  433. hashCols = [[maxName, 30].min, "CodeBlock".size].max
  434. remaining -= hashCols + 1
  435. countCols = [maxCountDigits * $engines.size, "Source Counts".size].max
  436. remaining -= countCols + 1
  437. if mode == :full
  438. instructionCountCols = 6
  439. remaining -= instructionCountCols + 1
  440. machineCountCols = [maxCountDigits * $engines.size, "Machine Counts".size].max
  441. remaining -= machineCountCols + 1
  442. compilationsCols = 7
  443. remaining -= compilationsCols + 1
  444. inlinesCols = 9
  445. remaining -= inlinesCols + 1
  446. exitCountCols = 7
  447. remaining -= exitCountCols + 1
  448. recentOptsCols = 12
  449. remaining -= recentOptsCols + 1
  450. end
  451. if remaining > 0
  452. sourceCols = remaining
  453. else
  454. sourceCols = nil
  455. end
  456. print(center("CodeBlock", hashCols))
  457. if mode == :full
  458. print(" " + center("#Instr", instructionCountCols))
  459. end
  460. print(" " + center("Source Counts", countCols))
  461. if mode == :full
  462. print(" " + center("Machine Counts", machineCountCols))
  463. print(" " + center("#Compil", compilationsCols))
  464. print(" " + center("Inlines", inlinesCols))
  465. print(" " + center("#Exits", exitCountCols))
  466. print(" " + center("Last Opts", recentOptsCols))
  467. end
  468. if sourceCols
  469. print(" " + center("Source", sourceCols))
  470. end
  471. puts
  472. print(center("", hashCols))
  473. if mode == :full
  474. print(" " + (" " * instructionCountCols))
  475. end
  476. print(" " + center("Base/DFG", countCols))
  477. if mode == :full
  478. print(" " + center("Base/DFG", machineCountCols))
  479. print(" " + (" " * compilationsCols))
  480. print(" " + center("Src/Total", inlinesCols))
  481. print(" " + (" " * exitCountCols))
  482. print(" " + center("Get/Put/Call", recentOptsCols))
  483. end
  484. puts
  485. $bytecodes.sort {
  486. | a, b |
  487. b.totalMaxTopExecutionCount <=> a.totalMaxTopExecutionCount
  488. }.each {
  489. | bytecode |
  490. print(center(bytecode.name(hashCols), hashCols))
  491. if mode == :full
  492. print(" " + center(bytecode.instructionCount.to_s, instructionCountCols))
  493. end
  494. print(" " +
  495. center($engines.map {
  496. | engine |
  497. bytecode.maxTopExecutionCount(engine).to_s
  498. }.join("/"), countCols))
  499. if mode == :full
  500. print(" " + center($engines.map {
  501. | engine |
  502. bytecode.maxBottomExecutionCount(engine).to_s
  503. }.join("/"), machineCountCols))
  504. print(" " + center(bytecode.compilations.size.to_s, compilationsCols))
  505. print(" " + center(bytecode.sourceMachineInlineSites.to_s + "/" + bytecode.totalMachineInlineSites.to_s, inlinesCols))
  506. print(" " + center(bytecode.totalExitCount.to_s, exitCountCols))
  507. lastCompilation = bytecode.compilations[-1]
  508. if lastCompilation
  509. optData = [lastCompilation.numInlinedGetByIds,
  510. lastCompilation.numInlinedPutByIds,
  511. lastCompilation.numInlinedCalls]
  512. else
  513. optData = ["N/A"]
  514. end
  515. print(" " + center(optData.join('/'), recentOptsCols))
  516. end
  517. if sourceCols
  518. print(" " + sourceOnOneLine(bytecode.source, sourceCols))
  519. end
  520. puts
  521. }
  522. end
  523. def executeCommand(*commandArray)
  524. command = commandArray[0]
  525. args = commandArray[1..-1]
  526. case command
  527. when "help", "h", "?"
  528. puts "summary (s) Print a summary of code block execution rates."
  529. puts "full (f) Same as summary, but prints more information."
  530. puts "source Show the source for a code block."
  531. puts "bytecode (b) Show the bytecode for a code block, with counts."
  532. puts "profiling (p) Show the (internal) profiling data for a code block."
  533. puts "display (d) Display details for a code block."
  534. puts "inlines Show all inlining stacks that the code block was on."
  535. puts "help (h) Print this message."
  536. puts "quit (q) Quit."
  537. when "quit", "q", "exit"
  538. exit 0
  539. when "summary", "s"
  540. summary(:summary)
  541. when "full", "f"
  542. summary(:full)
  543. when "source"
  544. if args.length != 1
  545. puts "Usage: source <code block hash>"
  546. return
  547. end
  548. $bytecodes.each {
  549. | bytecode |
  550. if bytecode.matches(args[0])
  551. puts bytecode.source
  552. end
  553. }
  554. when "bytecode", "b"
  555. if args.length != 1
  556. puts "Usage: source <code block hash>"
  557. return
  558. end
  559. hash = args[0]
  560. countCols = 10 * $engines.size
  561. machineCols = 10 * $engines.size
  562. pad = 1
  563. while (countCols + 1 + machineCols + pad) % 8 != 0
  564. pad += 1
  565. end
  566. $bytecodes.each {
  567. | bytecodes |
  568. next unless bytecodes.matches(hash)
  569. puts(center("Source Counts", countCols) + " " + center("Machine Counts", machineCols) +
  570. (" " * pad) + center("Bytecode for #{bytecodes}", screenWidth - pad - countCols - 1 - machineCols))
  571. puts(center("Base/DFG", countCols) + " " + center("Base/DFG", countCols))
  572. bytecodes.each {
  573. | bytecode |
  574. if bytecode.shouldHaveCounts?
  575. countsString = $engines.map {
  576. | myEngine |
  577. bytecode.topExecutionCount(myEngine)
  578. }.join("/")
  579. machineString = $engines.map {
  580. | myEngine |
  581. bytecode.bottomExecutionCount(myEngine)
  582. }.join("/")
  583. else
  584. countsString = ""
  585. machineString = ""
  586. end
  587. puts(center(countsString, countCols) + " " + center(machineString, machineCols) + (" " * pad) + bytecode.description.chomp)
  588. bytecode.osrExits.each {
  589. | exit |
  590. puts(center("!!!!!", countCols) + " " + center("!!!!!", machineCols) + (" " * (pad + 10)) +
  591. "EXIT: in #{exit.compilation} due to #{exit.exitKind}, #{exit.count} times")
  592. }
  593. }
  594. }
  595. when "profiling", "p"
  596. if args.length != 1
  597. puts "Usage: profiling <code block hash>"
  598. return
  599. end
  600. hash = args[0]
  601. first = true
  602. $compilations.each {
  603. | compilation |
  604. compilation.profiledBytecodes.each {
  605. | profiledBytecodes |
  606. if profiledBytecodes.bytecodes.matches(hash)
  607. if first
  608. first = false
  609. else
  610. puts
  611. end
  612. puts "Compilation #{compilation}:"
  613. profiledBytecodes.header.each {
  614. | header |
  615. puts(" " * 6 + header)
  616. }
  617. profiledBytecodes.each {
  618. | bytecode |
  619. puts(" " * 8 + bytecode.description)
  620. profiledBytecodes.bytecodes.bytecode(bytecode.bytecodeIndex).osrExits.each {
  621. | exit |
  622. if exit.compilation == compilation
  623. puts(" !!!!! EXIT: due to #{exit.exitKind}, #{exit.count} times")
  624. end
  625. }
  626. }
  627. end
  628. }
  629. }
  630. when "inlines"
  631. if args.length != 1
  632. puts "Usage: inlines <code block hash>"
  633. return
  634. end
  635. hash = args[0]
  636. $bytecodes.each {
  637. | bytecodes |
  638. next unless bytecodes.matches(hash)
  639. # FIXME: print something useful to say more about which code block this is.
  640. $compilations.each {
  641. | compilation |
  642. myOrigins = []
  643. compilation.descriptions.each {
  644. | description |
  645. if description.origin.index {
  646. | myBytecode |
  647. bytecodes == myBytecode.bytecodes
  648. }
  649. myOrigins << description.origin
  650. end
  651. }
  652. myOrigins.uniq!
  653. myOrigins.sort! {
  654. | a, b |
  655. result = 0
  656. [a.size, b.size].min.times {
  657. | index |
  658. result = a[index].bytecodeIndex <=> b[index].bytecodeIndex
  659. break if result != 0
  660. }
  661. result
  662. }
  663. next if myOrigins.empty?
  664. printArray = []
  665. lastPrintStack = []
  666. def originToPrintStack(origin)
  667. (0...(origin.size - 1)).map {
  668. | index |
  669. "bc\##{origin[index].bytecodeIndex} --> #{origin[index + 1].bytecodes}"
  670. }
  671. end
  672. def printStack(printArray, stack, lastStack)
  673. stillCommon = true
  674. stack.each_with_index {
  675. | entry, index |
  676. next if stillCommon and entry == lastStack[index]
  677. printArray << (" " * (index + 1) + entry)
  678. stillCommon = false
  679. }
  680. end
  681. myOrigins.each {
  682. | origin |
  683. currentPrintStack = originToPrintStack(origin)
  684. printStack(printArray, currentPrintStack, lastPrintStack)
  685. lastPrintStack = currentPrintStack
  686. }
  687. next if printArray.empty?
  688. puts "Compilation #{compilation}:"
  689. printArray.each {
  690. | entry |
  691. puts entry
  692. }
  693. }
  694. }
  695. when "display", "d"
  696. compilationIndex = nil
  697. case args.length
  698. when 1
  699. if args[0] == "*"
  700. hash = nil
  701. else
  702. hash = args[0]
  703. end
  704. engine = nil
  705. when 2
  706. if mayBeHash(args[0])
  707. hash = args[0]
  708. engine = args[1]
  709. else
  710. engine = args[0]
  711. hash = args[1]
  712. end
  713. else
  714. puts "Usage: summary <code block hash> <engine>"
  715. return
  716. end
  717. if hash and hash =~ /-([0-9]+)-/
  718. hash = $~.pre_match
  719. engine = $~.post_match
  720. compilationIndex = $1.to_i
  721. end
  722. if engine and not $engines.index(engine)
  723. pattern = Regexp.new(Regexp.escape(engine), "i")
  724. trueEngine = nil
  725. $engines.each {
  726. | myEngine |
  727. if myEngine =~ pattern
  728. trueEngine = myEngine
  729. break
  730. end
  731. }
  732. unless trueEngine
  733. puts "#{engine} is not a valid engine, try #{$engines.join(' or ')}."
  734. return
  735. end
  736. engine = trueEngine
  737. end
  738. actualCountCols = 13
  739. sourceCountCols = 10 * $engines.size
  740. first = true
  741. $compilations.each {
  742. | compilation |
  743. next if hash and not compilation.bytecode.matches(hash)
  744. next if engine and compilation.engine != engine
  745. next if compilationIndex and compilation.compilationIndex != compilationIndex
  746. if first
  747. first = false
  748. else
  749. puts
  750. end
  751. puts("Compilation #{compilation}:")
  752. puts(" Num inlined: GetByIds: #{compilation.numInlinedGetByIds} PutByIds: #{compilation.numInlinedPutByIds} Calls: #{compilation.numInlinedCalls}")
  753. puts(center("Actual Counts", actualCountCols) + " " + center("Source Counts", sourceCountCols) + " " + center("Disassembly in #{compilation.engine}", screenWidth - 1 - sourceCountCols - 1 - actualCountCols))
  754. puts((" " * actualCountCols) + " " + center("Base/DFG", sourceCountCols))
  755. lines = []
  756. compilation.descriptions.each {
  757. | description |
  758. # FIXME: We should have a better way of detecting things like CountExecution nodes
  759. # and slow path entries in the baseline JIT.
  760. if description.description =~ /CountExecution\(/ and compilation.engine == "DFG"
  761. shouldShow = false
  762. else
  763. shouldShow = true
  764. end
  765. if description.origin.empty? or not description.origin[-1].shouldHaveCounts? or (compilation.engine == "Baseline" and description.description =~ /^\s*\(S\)/)
  766. actualCountsString = ""
  767. sourceCountsString = ""
  768. else
  769. actualCountsString = compilation.counter(description.origin).count.to_s
  770. sourceCountsString = $engines.map {
  771. | myEngine |
  772. description.origin[-1].topExecutionCount(myEngine)
  773. }.join("/")
  774. end
  775. description.description.split("\n").each {
  776. | line |
  777. lines << DescriptionLine.new(actualCountsString, sourceCountsString, line.chomp, shouldShow)
  778. }
  779. }
  780. exitPrefix = center("!!!!!", actualCountCols) + " " + center("!!!!!", sourceCountCols) + (" " * 25)
  781. lines.each_with_index {
  782. | line, index |
  783. codeAddress = line.codeAddress
  784. if codeAddress
  785. list = compilation.osrExits[codeAddress]
  786. if list
  787. list.each {
  788. | exit |
  789. if exit.isWatchpoint
  790. exit.dumpForDisplay(exitPrefix)
  791. end
  792. }
  793. end
  794. end
  795. if line.shouldShow
  796. puts(center(line.actualCountsString, actualCountCols) + " " + center(line.sourceCountsString, sourceCountCols) + " " + line.disassembly)
  797. end
  798. if codeAddress
  799. # Find the next disassembly address.
  800. endIndex = index + 1
  801. endAddress = nil
  802. while endIndex < lines.size
  803. myAddress = lines[endIndex].codeAddress
  804. if myAddress
  805. endAddress = myAddress
  806. break
  807. end
  808. endIndex += 1
  809. end
  810. if endAddress
  811. list = compilation.osrExits[endAddress]
  812. if list
  813. list.each {
  814. | exit |
  815. unless exit.isWatchpoint
  816. exit.dumpForDisplay(exitPrefix)
  817. end
  818. }
  819. end
  820. end
  821. end
  822. }
  823. }
  824. else
  825. puts "Invalid command: #{command}"
  826. end
  827. end
  828. if $stdin.tty?
  829. executeCommand("full")
  830. end
  831. while commandLine = Readline.readline("> ", true)
  832. executeCommand(*commandLine.split)
  833. end