Skip to content

Commit

Permalink
Merge pull request #14815 from JuliaLang/jn/debug
Browse files Browse the repository at this point in the history
track the exact lambda associated with a fptr
  • Loading branch information
vtjnash committed Jan 31, 2016
2 parents 7f1ad6c + 8a0a7d6 commit 278d128
Show file tree
Hide file tree
Showing 23 changed files with 824 additions and 208 deletions.
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ Library improvements

* Improve performance of `quantile` ([#14413]).

* The new `Base.StackTraces` module makes stack traces easier to use programmatically. ([#14469])

Deprecated or removed
---------------------

Expand Down Expand Up @@ -1779,3 +1781,4 @@ Too numerous to mention.
[#14424]: https://github.com/JuliaLang/julia/issues/14424
[#14759]: https://github.com/JuliaLang/julia/issues/14759
[#14114]: https://github.com/JuliaLang/julia/issues/14114
[#14469]: https://github.com/JuliaLang/julia/issues/14469
7 changes: 3 additions & 4 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,12 @@ function firstcaller(bt::Array{Ptr{Void},1}, funcsym::Symbol)
# Identify the calling line
i = 1
while i <= length(bt)
lkup = ccall(:jl_lookup_code_address, Any, (Ptr{Void},Cint), bt[i], true)
lkup = StackTraces.lookup(bt[i])
i += 1
if lkup === ()
if lkup === StackTraces.UNKNOWN
continue
end
fname, file, line, inlinedat_file, inlinedat_line, fromC = lkup
if fname == funcsym
if lkup.func == funcsym
break
end
end
Expand Down
7 changes: 7 additions & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export
Pkg,
Git,
LibGit2,
StackTraces,
Profile,
Dates,
Sys,
Expand Down Expand Up @@ -1046,6 +1047,12 @@ export
rethrow,
systemerror,

# stack traces
StackTrace,
StackFrame,
stacktrace,
catch_stacktrace,

# types
convert,
fieldoffset,
Expand Down
125 changes: 53 additions & 72 deletions base/profile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Profile

import Base: hash, ==
import Base.StackTraces: lookup, UNKNOWN, show_spec_linfo

export @profile

Expand Down Expand Up @@ -117,12 +117,12 @@ end

function getdict(data::Vector{UInt})
uip = unique(data)
Dict{UInt, LineInfo}([ip=>lookup(ip) for ip in uip])
Dict{UInt, StackFrame}([ip=>lookup(ip) for ip in uip])
end

# TODO update signature in docstring.
"""
callers(funcname, [data, lidict], [filename=<filename>], [linerange=<start:stop>]) -> Vector{Tuple{count, linfo}}
callers(funcname, [data, lidict], [filename=<filename>], [linerange=<start:stop>]) -> Vector{Tuple{count, lineinfo}}
Given a previous profiling run, determine who called a particular function. Supplying the
filename (and optionally, range of line numbers over which the function is defined) allows
Expand Down Expand Up @@ -165,36 +165,6 @@ Julia, and examine the resulting `*.mem` files.
"""
clear_malloc_data() = ccall(:jl_clear_malloc_data, Void, ())


####
#### Internal interface
####
immutable LineInfo
func::ByteString
file::ByteString
line::Int
inlined_file::ByteString
inlined_line::Int
fromC::Bool
ip::Int64 # large enough that this struct can be losslessly read on any machine (32 or 64 bit)
end

const UNKNOWN = LineInfo("?", "?", -1, "?", -1, true, 0)

#
# If the LineInfo has function and line information, we consider two of them the same
# if they share the same function/line information. For unknown functions, line==ip
# so we never actually need to consider the .ip field.
#
==(a::LineInfo, b::LineInfo) = a.line == b.line && a.fromC == b.fromC && a.func == b.func && a.file == b.file

function hash(li::LineInfo, h::UInt)
h += 0xf4fbda67fe20ce88 % UInt
h = hash(li.line, h)
h = hash(li.file, h)
h = hash(li.func, h)
end

# C wrappers
start_timer() = ccall(:jl_profile_start_timer, Cint, ())

Expand All @@ -208,16 +178,6 @@ len_data() = convert(Int, ccall(:jl_profile_len_data, Csize_t, ()))

maxlen_data() = convert(Int, ccall(:jl_profile_maxlen_data, Csize_t, ()))

function lookup(ip::Ptr{Void})
info = ccall(:jl_lookup_code_address, Any, (Ptr{Void},Cint), ip, false)
if length(info) == 7
return LineInfo(string(info[1]), string(info[2]), Int(info[3]), string(info[4]), Int(info[5]), info[6], Int64(info[7]))
else
return UNKNOWN
end
end
lookup(ip::UInt) = lookup(convert(Ptr{Void},ip))

error_codes = Dict{Int,ASCIIString}(
-1=>"cannot specify signal action for profiling",
-2=>"cannot create the timer for profiling",
Expand Down Expand Up @@ -282,7 +242,7 @@ function parse_flat(iplist, n, lidict, C::Bool)
# The ones with no line number might appear multiple times in a single
# backtrace, giving the wrong impression about the total number of backtraces.
# Delete them too.
keep = !Bool[x == UNKNOWN || x.line == 0 || (x.fromC && !C) for x in lilist]
keep = !Bool[x == UNKNOWN || x.line == 0 || (x.from_c && !C) for x in lilist]
n = n[keep]
lilist = lilist[keep]
lilist, n
Expand All @@ -301,7 +261,7 @@ function flat{T<:Unsigned}(io::IO, data::Vector{T}, lidict::Dict, C::Bool, combi
print_flat(io, lilist, n, combine, cols, sortedby)
end

function print_flat(io::IO, lilist::Vector{LineInfo}, n::Vector{Int}, combine::Bool, cols::Integer, sortedby)
function print_flat(io::IO, lilist::Vector{StackFrame}, n::Vector{Int}, combine::Bool, cols::Integer, sortedby)
p = liperm(lilist)
lilist = lilist[p]
n = n[p]
Expand All @@ -326,26 +286,35 @@ function print_flat(io::IO, lilist::Vector{LineInfo}, n::Vector{Int}, combine::B
end
wcounts = max(6, ndigits(maximum(n)))
maxline = 0
maxfile = 0
maxfunc = 0
maxfile = 6
maxfunc = 10
for li in lilist
maxline = max(maxline, li.line)
maxfile = max(maxfile, length(li.file))
maxfunc = max(maxfunc, length(li.func))
maxfile = max(maxfile, length(string(li.file)))
maxfunc = max(maxfunc, length(string(li.func)))
end
wline = max(5, ndigits(maxline))
ntext = cols - wcounts - wline - 3
if maxfile+maxfunc <= ntext
maxfunc += 25
if maxfile + maxfunc <= ntext
wfile = maxfile
wfunc = maxfunc
else
wfile = floor(Integer,2*ntext/5)
wfunc = floor(Integer,3*ntext/5)
end
println(io, lpad("Count", wcounts, " "), " ", rpad("File", wfile, " "), " ", rpad("Function", wfunc, " "), " ", lpad("Line", wline, " "))
println(io, lpad("Count", wcounts, " "), " ", rpad("File", wfile, " "), " ", lpad("Line", wline, " "), " ", rpad("Function", wfunc, " "))
for i = 1:length(n)
li = lilist[i]
println(io, lpad(string(n[i]), wcounts, " "), " ", rpad(truncto(li.file, wfile), wfile, " "), " ", rpad(truncto(li.func, wfunc), wfunc, " "), " ", lpad(string(li.line), wline, " "))
Base.print(io, lpad(string(n[i]), wcounts, " "), " ")
Base.print(io, rpad(rtruncto(string(li.file), wfile), wfile, " "), " ")
Base.print(io, lpad(string(li.line), wline, " "), " ")
fname = string(li.func)
if !li.from_c && !isnull(li.outer_linfo)
fname = sprint(show_spec_linfo, li)
end
Base.print(io, rpad(ltruncto(fname, wfunc), wfunc, " "))
println(io)
end
end

Expand All @@ -371,9 +340,9 @@ function tree_aggregate{T<:Unsigned}(data::Vector{T})
bt, counts
end

tree_format_linewidth(x::LineInfo) = ndigits(x.line)+6
tree_format_linewidth(x::StackFrame) = ndigits(x.line)+6

function tree_format(lilist::Vector{LineInfo}, counts::Vector{Int}, level::Int, cols::Integer)
function tree_format(lilist::Vector{StackFrame}, counts::Vector{Int}, level::Int, cols::Integer)
nindent = min(cols>>1, level)
ndigcounts = ndigits(maximum(counts))
ndigline = maximum([tree_format_linewidth(x) for x in lilist])
Expand All @@ -394,24 +363,26 @@ function tree_format(lilist::Vector{LineInfo}, counts::Vector{Int}, level::Int,
if showextra
base = string(base, "+", nextra, " ")
end
if li.line == li.ip
if li.line == li.pointer
strs[i] = string(base,
rpad(string(counts[i]), ndigcounts, " "),
" ","unknown function (ip: 0x",hex(li.ip,2*sizeof(Ptr{Void})),
" ",
"unknown function (pointer: 0x",
hex(li.pointer,2*sizeof(Ptr{Void})),
")")
else
base = string(base,
fname = string(li.func)
if !li.from_c && !isnull(li.outer_linfo)
fname = sprint(show_spec_linfo, li)
end
strs[i] = string(base,
rpad(string(counts[i]), ndigcounts, " "),
" ",
truncto(string(li.file), widthfile),
rtruncto(string(li.file), widthfile),
":",
li.line == -1 ? "?" : string(li.line),
"; ",
truncto(string(li.func), widthfunc),
"; ")
if li.line == -1
strs[i] = string(base, "(unknown line)")
else
strs[i] = string(base, "line: ", li.line)
end
ltruncto(fname, widthfunc))
end
else
strs[i] = ""
Expand All @@ -428,7 +399,7 @@ function tree{T<:Unsigned}(io::IO, bt::Vector{Vector{T}}, counts::Vector{Int}, l
# Organize backtraces into groups that are identical up to this level
if combine
# Combine based on the line information
d = Dict{LineInfo,Vector{Int}}()
d = Dict{StackFrame,Vector{Int}}()
for i = 1:length(bt)
ip = bt[i][level+1]
key = lidict[ip]
Expand All @@ -441,7 +412,7 @@ function tree{T<:Unsigned}(io::IO, bt::Vector{Vector{T}}, counts::Vector{Int}, l
end
# Generate counts
dlen = length(d)
lilist = Array(LineInfo, dlen)
lilist = Array(StackFrame, dlen)
group = Array(Vector{Int}, dlen)
n = Array(Int, dlen)
i = 1
Expand All @@ -465,7 +436,7 @@ function tree{T<:Unsigned}(io::IO, bt::Vector{Vector{T}}, counts::Vector{Int}, l
end
# Generate counts, and do the code lookup
dlen = length(d)
lilist = Array(LineInfo, dlen)
lilist = Array(StackFrame, dlen)
group = Array(Vector{Int}, dlen)
n = Array(Int, dlen)
i = 1
Expand Down Expand Up @@ -516,7 +487,7 @@ function tree{T<:Unsigned}(io::IO, data::Vector{T}, lidict::Dict, C::Bool, combi
end

function callersf(matchfunc::Function, bt::Vector{UInt}, lidict)
counts = Dict{LineInfo, Int}()
counts = Dict{StackFrame, Int}()
lastmatched = false
for id in bt
if id == 0
Expand All @@ -540,16 +511,26 @@ function callersf(matchfunc::Function, bt::Vector{UInt}, lidict)
end

# Utilities
function truncto(str::ByteString, w::Int)
function rtruncto(str::ByteString, w::Int)
ret = str;
if length(str) > w
ret = string("...", str[end-w+4:end])
end
ret
end
function ltruncto(str::ByteString, w::Int)
ret = str;
if length(str) > w
ret = string(str[1:w-4], "...")
end
ret
end


truncto(str::Symbol, w::Int) = truncto(string(str), w)

# Order alphabetically (file, function) and then by line number
function liperm(lilist::Vector{LineInfo})
function liperm(lilist::Vector{StackFrame})
comb = Array(ByteString, length(lilist))
for i = 1:length(lilist)
li = lilist[i]
Expand All @@ -568,7 +549,7 @@ warning_empty() = warn("""
Profile.init().""")

function purgeC(data, lidict)
keep = Bool[d == 0 || lidict[d].fromC == false for d in data]
keep = Bool[d == 0 || lidict[d].from_c == false for d in data]
data[keep]
end

Expand Down
Loading

0 comments on commit 278d128

Please sign in to comment.