better debugging support for native debuggers; changed name mangling; fixes #3471
This commit is contained in:
parent
97129ebd8a
commit
b654aa399a
3 changed files with 164 additions and 58 deletions
|
|
@ -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,21 +28,24 @@ 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)
|
||||||
# XXX: This is still very experimental
|
|
||||||
#
|
|
||||||
# Even with all these inefficient checks, the bootstrap
|
# Even with all these inefficient checks, the bootstrap
|
||||||
# time is actually improved. This is probably because so many
|
# time is actually improved. This is probably because so many
|
||||||
# rope concatenations are now eliminated.
|
# rope concatenations are now eliminated.
|
||||||
#
|
#
|
||||||
# Future notes:
|
# sfFromGeneric is needed in order to avoid multiple
|
||||||
# sfFromGeneric seems to be needed in order to avoid multiple
|
|
||||||
# definitions of certain variables generated in transf with
|
# definitions of certain variables generated in transf with
|
||||||
# names such as:
|
# names such as:
|
||||||
# `r`, `res`
|
# `r`, `res`
|
||||||
|
|
@ -66,8 +71,6 @@ proc mangleName(s: PSym): Rope =
|
||||||
# about isCKeyword:
|
# about isCKeyword:
|
||||||
# Nim variable names can be C keywords.
|
# Nim variable names can be C keywords.
|
||||||
# We need to avoid such names in the generated code.
|
# 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:
|
# about sfGlobal:
|
||||||
# This seems to be harder - a top level extern variable from
|
# This seems to be harder - a top level extern variable from
|
||||||
|
|
@ -77,16 +80,14 @@ proc mangleName(s: PSym): Rope =
|
||||||
# about skForVar:
|
# about skForVar:
|
||||||
# These are not properly scoped now - we need to add blocks
|
# These are not properly scoped now - we need to add blocks
|
||||||
# around for loops in transf
|
# around for loops in transf
|
||||||
if keepOrigName:
|
|
||||||
result = s.name.s.mangle.rope
|
result = s.name.s.mangle.rope
|
||||||
|
if keepOrigName:
|
||||||
|
result.add "0"
|
||||||
else:
|
else:
|
||||||
add(result, rope(mangle(s.name.s)))
|
|
||||||
add(result, ~"_")
|
add(result, ~"_")
|
||||||
add(result, rope(s.id))
|
add(result, rope(s.id))
|
||||||
else:
|
|
||||||
add(result, rope(mangle(s.name.s)))
|
|
||||||
add(result, ~"_")
|
add(result, ~"_")
|
||||||
add(result, rope(s.id))
|
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
89
compiler/debuginfo.nim
Normal 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)
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue