better debugging support for native debuggers; changed name mangling; fixes #3471

This commit is contained in:
Araq 2016-05-10 22:02:56 +02:00
commit b654aa399a
3 changed files with 164 additions and 58 deletions

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2016 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -11,6 +11,8 @@
# ------------------------- Name Mangling -------------------------------- # ------------------------- Name Mangling --------------------------------
import debuginfo
proc isKeyword(w: PIdent): bool = proc isKeyword(w: PIdent): bool =
# Nim and C++ share some keywords # Nim and C++ share some keywords
# it's more efficient to test the whole Nim keywords range # it's more efficient to test the whole Nim keywords range
@ -26,67 +28,66 @@ proc mangleField(name: PIdent): string =
result[0] = result[0].toUpper # Mangling makes everything lowercase, result[0] = result[0].toUpper # Mangling makes everything lowercase,
# but some identifiers are C keywords # but some identifiers are C keywords
proc hashOwner(s: PSym): FilenameHash =
var m = s
while m.kind != skModule: m = m.owner
let p = m.owner
assert p.kind == skPackage
result = gDebugInfo.register(p.name.s, m.name.s)
proc mangleName(s: PSym): Rope = proc mangleName(s: PSym): Rope =
result = s.loc.r result = s.loc.r
if result == nil: if result == nil:
when oKeepVariableNames: let keepOrigName = s.kind in skLocalVars - {skForVar} and
let keepOrigName = s.kind in skLocalVars - {skForVar} and {sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and
{sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and not isKeyword(s.name)
not isKeyword(s.name) # Even with all these inefficient checks, the bootstrap
# XXX: This is still very experimental # time is actually improved. This is probably because so many
# # rope concatenations are now eliminated.
# Even with all these inefficient checks, the bootstrap #
# time is actually improved. This is probably because so many # sfFromGeneric is needed in order to avoid multiple
# rope concatenations are now eliminated. # definitions of certain variables generated in transf with
# # names such as:
# Future notes: # `r`, `res`
# sfFromGeneric seems to be needed in order to avoid multiple # I need to study where these come from.
# definitions of certain variables generated in transf with #
# names such as: # about sfShadowed:
# `r`, `res` # consider the following Nim code:
# I need to study where these come from. # var x = 10
# # block:
# about sfShadowed: # var x = something(x)
# consider the following Nim code: # The generated C code will be:
# var x = 10 # NI x;
# block: # x = 10;
# var x = something(x) # {
# The generated C code will be: # NI x;
# NI x; # x = something(x); // Oops, x is already shadowed here
# x = 10; # }
# { # Right now, we work-around by not keeping the original name
# NI x; # of the shadowed variable, but we can do better - we can
# x = something(x); // Oops, x is already shadowed here # create an alternative reference to it in the outer scope and
# } # use that in the inner scope.
# Right now, we work-around by not keeping the original name #
# of the shadowed variable, but we can do better - we can # about isCKeyword:
# create an alternative reference to it in the outer scope and # Nim variable names can be C keywords.
# use that in the inner scope. # We need to avoid such names in the generated code.
# #
# about isCKeyword: # about sfGlobal:
# Nim variable names can be C keywords. # This seems to be harder - a top level extern variable from
# We need to avoid such names in the generated code. # another modules can have the same name as a local one.
# XXX: Study whether mangleName is called just once per variable. # Maybe we should just implement sfShadowed for them too.
# Otherwise, there might be better place to do this. #
# # about skForVar:
# about sfGlobal: # These are not properly scoped now - we need to add blocks
# This seems to be harder - a top level extern variable from # around for loops in transf
# another modules can have the same name as a local one. result = s.name.s.mangle.rope
# Maybe we should just implement sfShadowed for them too. if keepOrigName:
# result.add "0"
# about skForVar:
# These are not properly scoped now - we need to add blocks
# around for loops in transf
if keepOrigName:
result = s.name.s.mangle.rope
else:
add(result, rope(mangle(s.name.s)))
add(result, ~"_")
add(result, rope(s.id))
else: else:
add(result, rope(mangle(s.name.s)))
add(result, ~"_") add(result, ~"_")
add(result, rope(s.id)) add(result, rope(s.id))
add(result, ~"_")
add(result, rope(hashOwner(s).BiggestInt))
s.loc.r = result s.loc.r = result
proc typeName(typ: PType): Rope = proc typeName(typ: PType): Rope =
@ -570,15 +571,27 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
let t = if t.kind == tyRange: t.lastSon else: t let t = if t.kind == tyRange: t.lastSon else: t
result = getTypeName(t) result = getTypeName(t)
idTablePut(m.typeCache, t, result) idTablePut(m.typeCache, t, result)
var size: int
if firstOrd(t) < 0: if firstOrd(t) < 0:
addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result]) addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
size = 4
else: else:
case int(getSize(typ)) size = int(getSize(t))
case size
of 1: addf(m.s[cfsTypes], "typedef NU8 $1;$n", [result]) of 1: addf(m.s[cfsTypes], "typedef NU8 $1;$n", [result])
of 2: addf(m.s[cfsTypes], "typedef NU16 $1;$n", [result]) of 2: addf(m.s[cfsTypes], "typedef NU16 $1;$n", [result])
of 4: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result]) of 4: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
of 8: addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result]) of 8: addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result])
else: internalError(t.sym.info, "getTypeDescAux: " & $getSize(t)) else: internalError(t.sym.info, "getTypeDescAux: enum")
let owner = hashOwner(t.sym)
if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner):
var vals: seq[(string, int)] = @[]
for i in countup(0, t.n.len - 1):
assert(t.n.sons[i].kind == nkSym)
let field = t.n.sons[i].sym
vals.add((field.name.s, field.position.int))
gDebugInfo.registerEnum(EnumDesc(size: size, owner: owner, id: t.sym.id,
name: t.sym.name.s, values: vals))
of tyProc: of tyProc:
result = getTypeName(t) result = getTypeName(t)
idTablePut(m.typeCache, t, result) idTablePut(m.typeCache, t, result)

89
compiler/debuginfo.nim Normal file
View file

@ -0,0 +1,89 @@
#
#
# The Nim Compiler
# (c) Copyright 2016 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## The compiler can generate debuginfo to help debuggers in translating back from C/C++/JS code
## to Nim. The data structure has been designed to produce something useful with Nim's marshal
## module.
type
FilenameHash* = uint32
FilenameMapping* = object
package*, file*: string
mangled*: FilenameHash
EnumDesc* = object
size*: int
owner*: FilenameHash
id*: int
name*: string
values*: seq[(string, int)]
DebugInfo* = object
version*: int
files*: seq[FilenameMapping]
enums*: seq[EnumDesc]
conflicts*: bool
{.experimental.}
using
self: var DebugInfo
package, file: string
{.this: self.}
proc sdbmHash(hash: FilenameHash, c: char): FilenameHash {.inline.} =
return FilenameHash(c) + (hash shl 6) + (hash shl 16) - hash
proc sdbmHash(package, file): FilenameHash =
template `&=`(x, c) = x = sdbmHash(x, c)
result = 0
for i in 0..<package.len:
result &= package[i]
result &= '.'
for i in 0..<file.len:
result &= file[i]
proc register*(self; package, file): FilenameHash =
result = sdbmHash(package, file)
for f in files:
if f.mangled == result:
if f.package == package and f.file == file: return
conflicts = true
break
files.add(FilenameMapping(package: package, file: file, mangled: result))
proc hasEnum*(self: DebugInfo; ename: string; id: int; owner: FilenameHash): bool =
for en in enums:
if en.owner == owner and en.name == ename and en.id == id: return true
proc registerEnum*(self; ed: EnumDesc) =
enums.add ed
proc init*(self) =
version = 1
files = @[]
enums = @[]
var gDebugInfo*: DebugInfo
debuginfo.init gDebugInfo
import marshal, streams
proc writeDebugInfo*(self; file) =
let s = newFileStream(file, fmWrite)
store(s, self)
s.close
proc writeDebugInfo*(file) = writeDebugInfo(gDebugInfo, file)
proc loadDebugInfo*(self; file) =
let s = newFileStream(file, fmRead)
load(s, self)
s.close
proc loadDebugInfo*(file) = loadDebugInfo(gDebugInfo, file)

View file

@ -16,6 +16,8 @@ import
lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs,
securehash, streams securehash, streams
from debuginfo import writeDebugInfo
type type
TSystemCC* = enum TSystemCC* = enum
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
@ -736,6 +738,8 @@ proc callCCompiler*(projectfile: string) =
if not noAbsolutePaths(): if not noAbsolutePaths():
if not exefile.isAbsolute(): if not exefile.isAbsolute():
exefile = joinPath(splitFile(projectfile).dir, exefile) exefile = joinPath(splitFile(projectfile).dir, exefile)
if optCDebug in gGlobalOptions:
writeDebugInfo(exefile.changeFileExt("ndb"))
exefile = quoteShell(exefile) exefile = quoteShell(exefile)
let linkOptions = getLinkOptions() & " " & let linkOptions = getLinkOptions() & " " &
getConfigVar(cCompiler, ".options.linker") getConfigVar(cCompiler, ".options.linker")