blob: e07e2736e05047316c735e7ed74300c158a75feb [file] [log] [blame] [edit]
#!/usr/bin/env ruby
# Copyright (C) 2023 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
require 'getoptlong'
require 'json'
$samplingProfilerIgnoreExternalSourceID = false
$samplingProfilerTopFunctionsCount = 12
$samplingProfilerTopBytecodesCount = 40
def usage
puts <<-HELP
Usage:
-s, --ignore-external-source-id:
Ignore external source id when aggregating.
-c n, --top-function-count n:
Show n items of top functions.
-b n, --top-bytecode-count n:
Show n items of top bytecodes.
-h, --help:
Show this help.
HELP
exit(0)
end
GetoptLong.new(
['--help', '-h', GetoptLong::NO_ARGUMENT],
['--ignore-external-source-id', '-s', GetoptLong::NO_ARGUMENT],
['--top-function-count', '-c', GetoptLong::REQUIRED_ARGUMENT],
['--top-bytecode-count', '-b', GetoptLong::REQUIRED_ARGUMENT],
).each {
| opt, arg |
case opt
when '--help'
usage
when '--ignore-external-source-id'
$samplingProfilerIgnoreExternalSourceID = true
when '--top-function-count'
$samplingProfilerTopFunctionsCount = arg.to_i
when '--top-bytecode-count'
$samplingProfilerTopBytecodesCount = arg.to_i
end
}
def reportTopFunctions database
totalSamples = 0
functionCounts = Hash.new(0)
database["traces"].each do |stackTrace|
next if stackTrace["frames"].empty?
frame = stackTrace["frames"][0]
hash, category, offset = frame["location"].split(":")
description = "#{frame["name"]}#{hash}"
unless $samplingProfilerIgnoreExternalSourceID
description = "#{description}:#{frame["sourceID"]}"
end
functionCounts[description] += 1
totalSamples += 1
end
def takeMax functionCounts
maxFrameDescription = nil
maxFrameCount = 0
functionCounts.each do |key, value|
if value > maxFrameCount
maxFrameCount = value
maxFrameDescription = key
end
end
if maxFrameDescription
functionCounts.delete(maxFrameDescription)
end
return maxFrameDescription, maxFrameCount
end
timingInterval = database["interval"] * 1000.0 * 1000.0
puts "Sampling rate: #{timingInterval} microseconds. Total samples: #{totalSamples}"
puts "Top functions as <numSamples 'functionName#hash:sourceID'>"
$samplingProfilerTopFunctionsCount.times do
key, value = takeMax(functionCounts)
break unless key
printf("%6u '%s'\n", value, key)
end
end
$tiers = {
:llint => "LLInt",
:baseline => "Baseline",
:dfg => "DFG",
:ftl => "FTL",
:ipint => "IPInt",
:wasmllint => "WasmLLInt",
:bbq => "BBQ",
:omg => "OMG",
:wasm => "Wasm",
:host => "Host",
:regexp => "RegExp",
:cpp => "C/C++",
:unknownFrame => "Unknown Frame",
:unknownExecutable => "Unknown Executable",
}
def reportTopBytecodes database
totalSamples = 0
bytecodeCounts = Hash.new(0)
tierCounts = Hash.new(0)
database["traces"].each do |stackTrace|
next if stackTrace["frames"].empty?
frame = stackTrace["frames"][0]
description = "#{frame["name"]}#{frame["location"]}"
inliner = frame["inliner"]
unless inliner.nil?
description = "#{description} <-- #{inliner["name"]}#{inliner["location"]}"
end
bytecodeCounts[description] += 1
tierCounts[frame["category"]] += 1
totalSamples += 1
end
def takeMax bytecodeCounts
maxFrameDescription = nil
maxFrameCount = 0
bytecodeCounts.each do |key, value|
if value > maxFrameCount
maxFrameCount = value
maxFrameDescription = key
end
end
if maxFrameDescription
bytecodeCounts.delete(maxFrameDescription)
end
return maxFrameDescription, maxFrameCount
end
timingInterval = database["interval"] * 1000.0 * 1000.0
puts "Sampling rate: #{timingInterval} microseconds. Total samples: #{totalSamples}"
puts ""
puts "Tier breakdown:"
puts "-----------------------------------"
$tiers.each do |key, value|
printf("%s %s (%0.06f%%)\n", (value + ':').ljust(20, ' '), tierCounts[value].to_s.rjust(10, ' '), tierCounts[value] * 100.0 / totalSamples.to_f)
end
puts ""
puts "Hottest bytecodes as <numSamples 'functionName#hash:JITType:bytecodeIndex'>"
$samplingProfilerTopBytecodesCount.times do
key, value = takeMax(bytecodeCounts)
break unless key
printf("%6u '%s'\n", value, key)
end
end
def main
json = nil
if ARGV.empty?
json = JSON.parse(STDIN.read)
else
json = JSON::parse(IO::read(ARGV[0]))
end
reportTopFunctions(json)
puts ""
reportTopBytecodes(json)
end
main
# vim: set sw=4 ts=4 et tw=80 :