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
parent 97129ebd8a
commit b654aa399a
3 changed files with 164 additions and 58 deletions

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf
# (c) Copyright 2016 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -11,6 +11,8 @@
# ------------------------- Name Mangling --------------------------------
import debuginfo
proc isKeyword(w: PIdent): bool =
# Nim and C++ share some keywords
# 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,
# 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 =
result = s.loc.r
if result == nil:
when oKeepVariableNames:
let keepOrigName = s.kind in skLocalVars - {skForVar} and
{sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and
not isKeyword(s.name)
# XXX: This is still very experimental
#
# Even with all these inefficient checks, the bootstrap
# time is actually improved. This is probably because so many
# rope concatenations are now eliminated.
#
# Future notes:
# sfFromGeneric seems to be needed in order to avoid multiple
# definitions of certain variables generated in transf with
# names such as:
# `r`, `res`
# I need to study where these come from.
#
# about sfShadowed:
# consider the following Nim code:
# var x = 10
# block:
# var x = something(x)
# The generated C code will be:
# NI x;
# x = 10;
# {
# NI x;
# x = something(x); // Oops, x is already shadowed here
# }
# Right now, we work-around by not keeping the original name
# of the shadowed variable, but we can do better - we can
# create an alternative reference to it in the outer scope and
# use that in the inner scope.
#
# about isCKeyword:
# Nim variable names can be C keywords.
# We need to avoid such names in the generated code.
# XXX: Study whether mangleName is called just once per variable.
# Otherwise, there might be better place to do this.
#
# about sfGlobal:
# This seems to be harder - a top level extern variable from
# another modules can have the same name as a local one.
# Maybe we should just implement sfShadowed for them too.
#
# 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))
let keepOrigName = s.kind in skLocalVars - {skForVar} and
{sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and
not isKeyword(s.name)
# Even with all these inefficient checks, the bootstrap
# time is actually improved. This is probably because so many
# rope concatenations are now eliminated.
#
# sfFromGeneric is needed in order to avoid multiple
# definitions of certain variables generated in transf with
# names such as:
# `r`, `res`
# I need to study where these come from.
#
# about sfShadowed:
# consider the following Nim code:
# var x = 10
# block:
# var x = something(x)
# The generated C code will be:
# NI x;
# x = 10;
# {
# NI x;
# x = something(x); // Oops, x is already shadowed here
# }
# Right now, we work-around by not keeping the original name
# of the shadowed variable, but we can do better - we can
# create an alternative reference to it in the outer scope and
# use that in the inner scope.
#
# about isCKeyword:
# Nim variable names can be C keywords.
# We need to avoid such names in the generated code.
#
# about sfGlobal:
# This seems to be harder - a top level extern variable from
# another modules can have the same name as a local one.
# Maybe we should just implement sfShadowed for them too.
#
# about skForVar:
# These are not properly scoped now - we need to add blocks
# around for loops in transf
result = s.name.s.mangle.rope
if keepOrigName:
result.add "0"
else:
add(result, rope(mangle(s.name.s)))
add(result, ~"_")
add(result, rope(s.id))
add(result, ~"_")
add(result, rope(hashOwner(s).BiggestInt))
s.loc.r = result
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
result = getTypeName(t)
idTablePut(m.typeCache, t, result)
var size: int
if firstOrd(t) < 0:
addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
size = 4
else:
case int(getSize(typ))
size = int(getSize(t))
case size
of 1: addf(m.s[cfsTypes], "typedef NU8 $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 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:
result = getTypeName(t)
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,
securehash, streams
from debuginfo import writeDebugInfo
type
TSystemCC* = enum
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
@@ -736,6 +738,8 @@ proc callCCompiler*(projectfile: string) =
if not noAbsolutePaths():
if not exefile.isAbsolute():
exefile = joinPath(splitFile(projectfile).dir, exefile)
if optCDebug in gGlobalOptions:
writeDebugInfo(exefile.changeFileExt("ndb"))
exefile = quoteShell(exefile)
let linkOptions = getLinkOptions() & " " &
getConfigVar(cCompiler, ".options.linker")