nimbus-eth1/nimbus/db/aristo/aristo_debug.nim

480 lines
14 KiB
Nim
Raw Normal View History

# nimbus-eth1
# Copyright (c) 2021 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
# http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed
# except according to those terms.
{.push raises: [].}
import
std/[algorithm, sequtils, sets, strutils, tables],
eth/[common, trie/nibbles],
stew/byteutils,
"."/[aristo_constants, aristo_desc, aristo_hike, aristo_init, aristo_vid],
./aristo_init/[aristo_memory, aristo_rocksdb]
# ------------------------------------------------------------------------------
# Ptivate functions
# ------------------------------------------------------------------------------
proc sortedKeys(lTab: Table[LeafTie,VertexID]): seq[LeafTie] =
lTab.keys.toSeq.sorted(cmp = proc(a,b: LeafTie): int = cmp(a,b))
proc sortedKeys(kMap: Table[VertexID,HashLabel]): seq[VertexID] =
kMap.keys.toSeq.mapIt(it.uint64).sorted.mapIt(it.VertexID)
proc sortedKeys(sTab: Table[VertexID,VertexRef]): seq[VertexID] =
sTab.keys.toSeq.mapIt(it.uint64).sorted.mapIt(it.VertexID)
proc sortedKeys(pPrf: HashSet[VertexID]): seq[VertexID] =
pPrf.toSeq.mapIt(it.uint64).sorted.mapIt(it.VertexID)
proc toPfx(indent: int; offset = 0): string =
if 0 < indent+offset: "\n" & " ".repeat(indent+offset) else: ""
proc labelVidUpdate(db: var AristoDb, lbl: HashLabel, vid: VertexID): string =
if lbl.key.isValid and vid.isValid:
if not db.top.isNil:
let lblVid = db.top.pAmk.getOrVoid lbl
if lblVid.isValid:
if lblVid != vid:
result = "(!)"
return
block:
let lblVid = db.xMap.getOrVoid lbl
if lblVid.isValid:
if lblVid != vid:
result = "(!)"
return
db.xMap[lbl] = vid
proc squeeze(s: string; hex = false; ignLen = false): string =
## For long strings print `begin..end` only
if hex:
let n = (s.len + 1) div 2
result = if s.len < 20: s else: s[0 .. 5] & ".." & s[s.len-8 .. ^1]
if not ignLen:
result &= "[" & (if 0 < n: "#" & $n else: "") & "]"
elif s.len <= 30:
result = s
else:
result = if (s.len and 1) == 0: s[0 ..< 8] else: "0" & s[0 ..< 7]
if not ignLen:
result &= "..(" & $s.len & ")"
result &= ".." & s[s.len-16 .. ^1]
proc stripZeros(a: string): string =
a.strip(leading=true, trailing=false, chars={'0'}).toLowerAscii
proc ppVid(vid: VertexID; pfx = true): string =
if pfx:
result = "$"
if vid.isValid:
result &= vid.uint64.toHex.stripZeros.toLowerAscii
else:
result &= "ø"
proc vidCode(lbl: HashLabel, db: AristoDb): uint64 =
if lbl.isValid:
if not db.top.isNil:
let vid = db.top.pAmk.getOrVoid lbl
if vid.isValid:
return vid.uint64
block:
let vid = db.xMap.getOrVoid lbl
if vid.isValid:
return vid.uint64
proc ppKey(key: HashKey): string =
if key == HashKey.default:
return "£ø"
if key == VOID_HASH_KEY:
return "£r"
if key == VOID_CODE_KEY:
return "£c"
"%" & key.ByteArray32
.mapIt(it.toHex(2)).join.tolowerAscii
.squeeze(hex=true,ignLen=true)
proc ppLabel(lbl: HashLabel; db: AristoDb): string =
if lbl.key == HashKey.default:
return "£ø"
if lbl.key == VOID_HASH_KEY:
return "£r"
if lbl.key == VOID_CODE_KEY:
return "£c"
let rid = if not lbl.root.isValid: "ø:"
else: ($lbl.root.uint64.toHex).stripZeros & ":"
if not db.top.isNil:
let vid = db.top.pAmk.getOrVoid lbl
if vid.isValid:
return "£" & rid & vid.ppVid(pfx=false)
block:
let vid = db.xMap.getOrVoid lbl
if vid.isValid:
return "£" & rid & vid.ppVid(pfx=false)
"%" & rid & lbl.key.ByteArray32
.mapIt(it.toHex(2)).join.tolowerAscii
.squeeze(hex=true,ignLen=true)
proc ppRootKey(a: HashKey): string =
if a.isValid:
return a.ppKey
proc ppCodeKey(a: HashKey): string =
if a != VOID_CODE_KEY:
return a.ppKey
proc ppLeafTie(lty: LeafTie, db: AristoDb): string =
if not db.top.isNil:
let vid = db.top.lTab.getOrVoid lty
if vid.isValid:
return "@" & vid.ppVid
"@" & ($lty.root.uint64.toHex).stripZeros & ":" &
lty.path.to(HashKey).ByteArray32
.mapIt(it.toHex(2)).join.squeeze(hex=true,ignLen=true)
proc ppPathPfx(pfx: NibblesSeq): string =
let s = $pfx
if s.len < 20: s else: s[0 .. 5] & ".." & s[s.len-8 .. ^1] & ":" & $s.len
proc ppNibble(n: int8): string =
if n < 0: "ø" elif n < 10: $n else: n.toHex(1).toLowerAscii
proc ppPayload(p: PayloadRef, db: AristoDb): string =
if p.isNil:
result = "n/a"
else:
case p.pType:
of BlobData:
result &= p.blob.toHex.squeeze(hex=true)
of AccountData:
result = "("
result &= $p.account.nonce & ","
result &= $p.account.balance & ","
result &= p.account.storageRoot.to(HashKey).ppRootKey() & ","
result &= p.account.codeHash.to(HashKey).ppCodeKey() & ")"
proc ppVtx(nd: VertexRef, db: AristoDb, vid: VertexID): string =
if not nd.isValid:
result = "n/a"
else:
if db.top.isNil or not vid.isValid or vid in db.top.pPrf:
result = ["L(", "X(", "B("][nd.vType.ord]
elif vid in db.top.kMap:
result = ["l(", "x(", "b("][nd.vType.ord]
else:
result = ["ł(", "€(", "þ("][nd.vType.ord]
case nd.vType:
of Leaf:
result &= nd.lPfx.ppPathPfx & "," & nd.lData.ppPayload(db)
of Extension:
result &= nd.ePfx.ppPathPfx & "," & nd.eVid.ppVid
of Branch:
for n in 0..15:
if nd.bVid[n].isValid:
result &= nd.bVid[n].ppVid
if n < 15:
result &= ","
result &= ")"
proc ppXMap*(
db: AristoDb;
kMap: Table[VertexID,HashLabel];
pAmk: Table[HashLabel,VertexID];
indent: int;
): string =
let dups = pAmk.values.toSeq.toCountTable.pairs.toSeq
.filterIt(1 < it[1]).toTable
proc ppNtry(n: uint64): string =
var s = "(" & VertexID(n).ppVid
let lbl = kMap.getOrVoid VertexID(n)
if lbl.isValid:
let vid = pAmk.getOrVoid lbl
if not vid.isValid:
s &= "," & lbl.ppLabel(db) & ""
elif vid != VertexID(n):
s &= "," & lbl.ppLabel(db) & "," & vid.ppVid
let count = dups.getOrDefault(VertexID(n), 0)
if 0 < count:
s &= ",*" & $count
else:
s &= "£r(!)"
s & "),"
var cache: seq[(uint64,uint64,bool)]
for vid in kMap.sortedKeys:
let lbl = kMap.getOrVoid vid
if lbl.isValid:
cache.add (vid.uint64, lbl.vidCode(db), 0 < dups.getOrDefault(vid, 0))
let lblVid = pAmk.getOrDefault(lbl, VertexID(0))
if lblVid != VertexID(0) and lblVid != vid:
cache[^1][2] = true
else:
cache.add (vid.uint64, 0u64, true)
result = "{"
if 0 < cache.len:
let
pfx = indent.toPfx(1)
var
(i, r) = (0, cache[0])
result &= cache[i][0].ppNtry
for n in 1 ..< cache.len:
let w = cache[n]
r[0].inc
r[1].inc
if r != w or w[2]:
if i+1 != n:
result &= ".. " & cache[n-1][0].ppNtry
result &= pfx & cache[n][0].ppNtry
(i, r) = (n, w)
if i < cache.len - 1:
if i+1 != cache.len - 1:
result &= ".. "
else:
result &= pfx
result &= cache[^1][0].ppNtry
result[^1] = '}'
else:
result &= "}"
proc ppBe[T](be: T; db: AristoDb; indent: int): string =
## Walk over backend tables
let pfx = indent.toPfx
result = "<" & $be.kind & ">"
result &= pfx & "vGen" & pfx & " [" & be.walkIdg.toSeq.mapIt(
it[2].pp
).join(",") & "]"
result &= pfx & "sTab" & pfx & " {" & be.walkVtx.toSeq.mapIt(
$(1+it[0]) & "(" & it[1].ppVid & "," & it[2].ppVtx(db,it[1]) & ")"
).join("," & pfx & " ") & "}"
result &= pfx & "kMap" & pfx & " {" & be.walkKey.toSeq.mapIt(
$(1+it[0]) & "(" & it[1].ppVid & "," & it[2].ppKey & ")"
).join("," & pfx & " ") & "}"
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc lblToVtxID*(db: var AristoDb, lbl: HashLabel): VertexID =
## Associate a vertex ID with the argument `key` for pretty printing.
if lbl.isValid:
let vid = db.xMap.getOrVoid lbl
if vid.isValid:
result = vid
else:
result = db.vidFetch()
db.xMap[lbl] = result
proc hashToVtxID*(db: var AristoDb, root: VertexID; hash: Hash256): VertexID =
db.lblToVtxID HashLabel(root: root, key: hash.to(HashKey))
proc pp*(key: HashKey): string =
key.ppKey
proc pp*(lbl: HashLabel, db = AristoDb()): string =
lbl.ppLabel(db)
proc pp*(lty: LeafTie, db = AristoDb()): string =
lty.ppLeafTie(db)
proc pp*(vid: VertexID): string =
vid.ppVid
proc pp*(vGen: openArray[VertexID]): string =
"[" & vGen.mapIt(it.ppVid).join(",") & "]"
proc pp*(p: PayloadRef, db = AristoDb()): string =
p.ppPayload(db)
proc pp*(nd: VertexRef, db = AristoDb()): string =
nd.ppVtx(db, VertexID(0))
proc pp*(nd: NodeRef; root: VertexID; db: var AristoDB): string =
if not nd.isValid:
result = "n/a"
elif nd.error != AristoError(0):
result = "(!" & $nd.error
else:
result = ["L(", "X(", "B("][nd.vType.ord]
case nd.vType:
of Leaf:
result &= $nd.lPfx.ppPathPfx & "," & nd.lData.pp(db)
of Extension:
let lbl = HashLabel(root: root, key: nd.key[0])
result &= $nd.ePfx.ppPathPfx & "," & nd.eVid.ppVid & ","
result &= lbl.ppLabel(db) & db.labelVidUpdate(lbl, nd.eVid)
of Branch:
result &= "["
for n in 0..15:
if nd.bVid[n].isValid or nd.key[n].isValid:
result &= nd.bVid[n].ppVid
let lbl = HashLabel(root: root, key: nd.key[n])
result &= db.labelVidUpdate(lbl, nd.bVid[n]) & ","
result[^1] = ']'
result &= ",["
for n in 0..15:
if nd.bVid[n].isValid or nd.key[n].isValid:
result &= HashLabel(root: root, key: nd.key[n]).ppLabel(db)
result &= ","
result[^1] = ']'
result &= ")"
proc pp*(nd: NodeRef): string =
var db = AristoDB()
nd.pp(db)
proc pp*(sTab: Table[VertexID,VertexRef]; db = AristoDb(); indent = 4): string =
"{" & sTab.sortedKeys
.mapIt((it, sTab.getOrVoid it))
.filterIt(it[1].isValid)
.mapIt("(" & it[0].ppVid & "," & it[1].ppVtx(db,it[0]) & ")")
.join("," & indent.toPfx(1)) & "}"
proc pp*(lTab: Table[LeafTie,VertexID]; indent = 4): string =
var db = AristoDb()
"{" & lTab.sortedKeys
.mapIt((it, lTab.getOrVoid it))
.filterIt(it[1].isValid)
.mapIt("(" & it[0].ppLeafTie(db) & "," & it[1].ppVid & ")")
.join("," & indent.toPfx(1)) & "}"
proc pp*(pPrf: HashSet[VertexID]): string =
"{" & pPrf.sortedKeys.mapIt(it.ppVid).join(",") & "}"
proc pp*(leg: Leg; db = AristoDb()): string =
result = "(" & leg.wp.vid.ppVid & ","
if not db.top.isNil:
let lbl = db.top.kMap.getOrVoid leg.wp.vid
if not lbl.isValid:
result &= "ø"
elif leg.wp.vid != db.top.pAmk.getOrVoid lbl:
result &= lbl.ppLabel(db)
result &= ","
if leg.backend:
result &= "*"
result &= ","
if 0 <= leg.nibble:
result &= $leg.nibble.ppNibble
result &= "," & leg.wp.vtx.pp(db) & ")"
proc pp*(hike: Hike; db = AristoDb(); indent = 4): string =
let pfx = indent.toPfx(1)
result = "["
if hike.legs.len == 0:
result &= "(" & hike.root.ppVid & ")"
else:
if hike.legs[0].wp.vid != hike.root:
result &= "(" & hike.root.ppVid & ")" & pfx
result &= hike.legs.mapIt(it.pp(db)).join(pfx)
result &= pfx & "(" & hike.tail.ppPathPfx & ")"
if hike.error != AristoError(0):
result &= pfx & "(" & $hike.error & ")"
result &= "]"
proc pp*(kMap: Table[VertexID,Hashlabel]; indent = 4): string =
var db: AristoDb
"{" & kMap.sortedKeys
.mapIt((it,kMap.getOrVoid it))
.filterIt(it[1].isValid)
.mapIt("(" & it[0].ppVid & "," & it[1].ppLabel(db) & ")")
.join("," & indent.toPfx(1)) & "}"
proc pp*(pAmk: Table[Hashlabel,VertexID]; indent = 4): string =
var
db: AristoDb
rev = pAmk.pairs.toSeq.mapIt((it[1],it[0])).toTable
"{" & rev.sortedKeys
.mapIt((it,rev.getOrVoid it))
.filterIt(it[1].isValid)
.mapIt("(" & it[1].ppLabel(db) & "," & it[0].ppVid & ")")
.join("," & indent.toPfx(1)) & "}"
proc pp*(kMap: Table[VertexID,Hashlabel]; db: AristoDb; indent = 4): string =
db.ppXMap(kMap, db.top.pAmk, indent)
proc pp*(pAmk: Table[Hashlabel,VertexID]; db: AristoDb; indent = 4): string =
db.ppXMap(db.top.kMap, pAmk, indent)
proc pp*(
be: MemBackendRef|RdbBackendRef;
db: AristoDb;
indent = 4;
): string =
be.ppBe(db, indent)
# ---------------------
proc pp*(
db: AristoDb;
sTabOk = true;
lTabOk = true;
kMapOk = true;
pPrfOk = true;
indent = 4;
): string =
let
pfx1 = indent.toPfx
pfx2 = indent.toPfx(1)
tagOk = 1 < sTabOk.ord + lTabOk.ord + kMapOk.ord + pPrfOk.ord
var
pfy = ""
proc doPrefix(s: string): string =
var rc: string
if tagOk:
rc = pfy & s & pfx2
pfy = pfx1
else:
rc = pfy
pfy = pfx2
rc
if not db.top.isNil:
if sTabOk:
let info = "sTab(" & $db.top.sTab.len & ")"
result &= info.doPrefix & db.top.sTab.pp(db,indent+1)
if lTabOk:
let info = "lTab(" & $db.top.lTab.len & ")"
result &= info.doPrefix & db.top.lTab.pp(indent+1)
if kMapOk:
let info = "kMap(" & $db.top.kMap.len & "," & $db.top.pAmk.len & ")"
result &= info.doPrefix & db.ppXMap(db.top.kMap,db.top.pAmk,indent+1)
if pPrfOk:
let info = "pPrf(" & $db.top.pPrf.len & ")"
result &= info.doPrefix & db.top.pPrf.pp
proc pp*(
be: AristoTypedBackendRef;
db: AristoDb;
indent = 4;
): string =
case (if be.isNil: BackendNone else: be.kind)
of BackendMemory:
be.MemBackendRef.ppBe(db, indent)
of BackendRocksDB:
be.RdbBackendRef.ppBe(db, indent)
of BackendNone:
"n/a"
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------