mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-22 16:33:02 -05:00
tools: parselog: parse winfsp logs
This commit is contained in:
parent
10053bc759
commit
fcfebb968f
318
tools/parselog.nim
Normal file
318
tools/parselog.nim
Normal file
@ -0,0 +1,318 @@
|
||||
# @file parselog.nim
|
||||
#
|
||||
# parse WinFsp debug logs
|
||||
#
|
||||
# @copyright 2015-2020 Bill Zissimopoulos
|
||||
|
||||
import algorithm
|
||||
import macros
|
||||
import parseopt
|
||||
import parseutils
|
||||
import strformat
|
||||
import strscans
|
||||
import strutils
|
||||
import tables
|
||||
|
||||
type
|
||||
Req = ref object
|
||||
fsname: string
|
||||
tid: uint32
|
||||
irp: uint64
|
||||
op: string
|
||||
inform: string
|
||||
context: string
|
||||
args: seq[(string, string)]
|
||||
Rsp = ref object
|
||||
fsname: string
|
||||
tid: uint32
|
||||
irp: uint64
|
||||
op: string
|
||||
status: uint32
|
||||
inform: uint
|
||||
context: string
|
||||
args: seq[(string, string)]
|
||||
|
||||
proc parseAndAddArg(prefix: string, rest: var string, args: var seq[(string, string)]) =
|
||||
discard scanf(rest, ",$*", rest)
|
||||
discard scanf(rest, "$s$*", rest)
|
||||
var n, v: string
|
||||
if scanf(rest, "\"$*\"$*", v, rest):
|
||||
args.add((prefix & "", v))
|
||||
elif scanf(rest, "$w=\"$*\"$*", n, v, rest):
|
||||
args.add((prefix & n, v))
|
||||
elif scanf(rest, "$w={$*}$*", n, v, rest):
|
||||
while "" != v:
|
||||
parseAndAddArg(n & ".", v, args)
|
||||
elif scanf(rest, "$w=$*,$*", n, v, rest):
|
||||
args.add((prefix & n, v))
|
||||
elif scanf(rest, "$w=$*$.", n, v):
|
||||
rest = ""
|
||||
args.add((prefix & n, v))
|
||||
elif scanf(rest, "$*,$*", v, rest):
|
||||
args.add((prefix & "", v))
|
||||
else:
|
||||
v = rest
|
||||
rest = ""
|
||||
args.add((prefix & "", v))
|
||||
|
||||
proc parseArgs(rest: var string): seq[(string, string)] =
|
||||
while "" != rest:
|
||||
parseAndAddArg("", rest, result)
|
||||
|
||||
proc parseReq(op, rest: string): Req =
|
||||
result = Req(op: op)
|
||||
var rest = rest
|
||||
var inform: string
|
||||
if scanf(rest, "[$+]$s$*", inform, rest):
|
||||
result.inform = inform
|
||||
var c0, c1: uint64
|
||||
if scanf(rest, "${parseHex[uint64]}:${parseHex[uint64]}$*", c0, c1, rest):
|
||||
result.context = toHex(c0) & ":" & toHex(c1)
|
||||
result.args = parseArgs(rest)
|
||||
|
||||
proc parseRsp(op, rest: string): Rsp =
|
||||
result = Rsp(op: op)
|
||||
var rest = rest
|
||||
var status: uint32
|
||||
var inform: uint
|
||||
if scanf(rest, "IoStatus=${parseHex[uint32]}[${parseUint}]$s$*", status, inform, rest):
|
||||
result.status = status
|
||||
result.inform = inform
|
||||
var c0, c1: uint64
|
||||
if scanf(rest, "UserContext=${parseHex[uint64]}:${parseHex[uint64]}$*", c0, c1, rest):
|
||||
result.context = toHex(c0) & ":" & toHex(c1)
|
||||
result.args = parseArgs(rest)
|
||||
|
||||
proc parseLog(path: string, processReq: proc(req: Req), processRsp: proc(rsp: Rsp)) =
|
||||
let file = open(path)
|
||||
defer: file.close()
|
||||
var lineno = 0
|
||||
try:
|
||||
for line in lines file:
|
||||
inc lineno
|
||||
var fsname, dir, op, rest: string
|
||||
var tid: uint32
|
||||
var irp: uint64
|
||||
var req: Req
|
||||
var rsp: Rsp
|
||||
if scanf(line, "$+[TID=${parseHex[uint32]}]:$s${parseHex[uint64]}:$s$+ $*",
|
||||
fsname, tid, irp, op, rest):
|
||||
dir = op[0..1]
|
||||
op = op[2..^1]
|
||||
case dir
|
||||
of ">>":
|
||||
req = parseReq(op, rest)
|
||||
req.fsname = fsname
|
||||
req.tid = tid
|
||||
req.irp = irp
|
||||
processReq(req)
|
||||
of "<<":
|
||||
rsp = parseRsp(op, rest)
|
||||
rsp.fsname = fsname
|
||||
rsp.tid = tid
|
||||
rsp.irp = irp
|
||||
processRsp(rsp)
|
||||
else:
|
||||
continue
|
||||
except:
|
||||
echo &"An exception has occurred while parsing file {path} line {lineno}"
|
||||
raise
|
||||
|
||||
type
|
||||
Stat = ref object
|
||||
ototal: int # open total
|
||||
oerror: int # open error total
|
||||
mtotal: int # multiplicate open total
|
||||
rtotal: int # read total
|
||||
rbytes: uint64 # read bytes
|
||||
rerror: int # read error total
|
||||
wtotal: int # write total
|
||||
wbytes: uint64 # write bytes
|
||||
werror: int # write error total
|
||||
dtotal: int # query directory total
|
||||
dbytes: uint64 # query directory bytes
|
||||
derror: int # query directory error total
|
||||
ptotal: int # query directory w/ pattern total
|
||||
pbytes: uint64 # query directory w/ pattern bytes
|
||||
perror: int # query directory w/ pattern error total
|
||||
ocount: int # current open count
|
||||
var
|
||||
reqtab = newTable[uint64, Req]()
|
||||
filetab = newTable[string, string]()
|
||||
stattab = newOrderedTable[string, Stat]()
|
||||
aggr = Stat()
|
||||
|
||||
proc getArg(args: seq[(string, string)], name: string): string =
|
||||
for n, v in items(args):
|
||||
if name == n:
|
||||
return v
|
||||
|
||||
proc processReq(req: Req) =
|
||||
reqtab[req.irp] = req
|
||||
case req.op
|
||||
of "Close":
|
||||
var filename: string
|
||||
if filetab.pop(req.context, filename):
|
||||
var stat = stattab.mgetOrPut(filename, Stat())
|
||||
stat.ocount -= 1
|
||||
|
||||
proc processRsp(rsp: Rsp) =
|
||||
var req: Req
|
||||
if reqtab.pop(rsp.irp, req):
|
||||
doAssert req.op == rsp.op
|
||||
doAssert req.irp == rsp.irp
|
||||
case req.op
|
||||
of "Create":
|
||||
var filename = getArg(req.args, "")
|
||||
if "" != filename:
|
||||
if 0 == rsp.status:
|
||||
filetab[rsp.context] = filename
|
||||
var stat = stattab.mgetOrPut(filename, Stat())
|
||||
stat.ototal += 1
|
||||
aggr.ototal += 1
|
||||
stat.ocount += 1
|
||||
if 2 == stat.ocount:
|
||||
stat.mtotal += 1
|
||||
aggr.mtotal += 1
|
||||
else:
|
||||
var stat = stattab.mgetOrPut(filename, Stat())
|
||||
stat.oerror += 1
|
||||
aggr.oerror += 1
|
||||
of "Read":
|
||||
var filename = filetab[req.context]
|
||||
var stat = stattab.mgetOrPut(filename, Stat())
|
||||
if 0 == rsp.status:
|
||||
stat.rtotal += 1
|
||||
stat.rbytes += rsp.inform
|
||||
aggr.rtotal += 1
|
||||
aggr.rbytes += rsp.inform
|
||||
elif 0xC0000011u32 != rsp.status:
|
||||
echo &"{rsp.status:X}"
|
||||
stat.rerror += 1
|
||||
aggr.rerror += 1
|
||||
of "Write":
|
||||
var filename = filetab[req.context]
|
||||
var stat = stattab.mgetOrPut(filename, Stat())
|
||||
if 0 == rsp.status:
|
||||
stat.wtotal += 1
|
||||
stat.wbytes += rsp.inform
|
||||
aggr.wtotal += 1
|
||||
aggr.wbytes += rsp.inform
|
||||
else:
|
||||
stat.werror += 1
|
||||
aggr.werror += 1
|
||||
of "QueryDirectory":
|
||||
var filename = filetab[req.context]
|
||||
var stat = stattab.mgetOrPut(filename, Stat())
|
||||
var pattern = getArg(req.args, "Pattern")
|
||||
if "NULL" == pattern:
|
||||
if 0 == rsp.status:
|
||||
stat.dtotal += 1
|
||||
stat.dbytes += rsp.inform
|
||||
aggr.dtotal += 1
|
||||
aggr.dbytes += rsp.inform
|
||||
else:
|
||||
stat.derror += 1
|
||||
aggr.derror += 1
|
||||
else:
|
||||
if 0 == rsp.status:
|
||||
stat.ptotal += 1
|
||||
stat.pbytes += rsp.inform
|
||||
aggr.ptotal += 1
|
||||
aggr.pbytes += rsp.inform
|
||||
else:
|
||||
stat.perror += 1
|
||||
aggr.perror += 1
|
||||
|
||||
macro identName(n: untyped): untyped =
|
||||
result = n.strVal.newLit
|
||||
|
||||
template dumpstat(F: untyped) =
|
||||
stattab.sort(proc (x, y: (string, Stat)): int =
|
||||
cmp(x[1].F, y[1].F), SortOrder.Descending)
|
||||
var width, rows = 0
|
||||
for filename, stat in stattab.pairs:
|
||||
if 0 == width:
|
||||
var s = identName(F).toUpperAscii()
|
||||
width = len($aggr.F)
|
||||
if width < len(s):
|
||||
width = len(s)
|
||||
var f: string
|
||||
formatValue(f, s, ">" & $width)
|
||||
echo f, " PER% FILENAME"
|
||||
var c0, c1: string
|
||||
formatValue(c0, stat.F, $width)
|
||||
if 0 != aggr.F:
|
||||
formatValue(c1, 100.0 * float(stat.F) / float(aggr.F), "5.1f")
|
||||
else:
|
||||
c1 = " "
|
||||
echo c0, " ", c1, " ", filename
|
||||
inc rows
|
||||
if opttop == rows:
|
||||
break
|
||||
var c0: string
|
||||
formatValue(c0, aggr.F, $width)
|
||||
echo c0, " 100.0 TOTAL"
|
||||
|
||||
proc main =
|
||||
var filenames: seq[string]
|
||||
var optstat: seq[string]
|
||||
var opttop = 0
|
||||
for kind, key, val in getopt(shortNoVal = {'Z'}, longNoVal = @["Zoo"]):
|
||||
case kind
|
||||
of cmdShortOption, cmdLongOption:
|
||||
case key
|
||||
of "stat":
|
||||
optstat.add(val)
|
||||
of "n":
|
||||
opttop = parseInt(val)
|
||||
of cmdArgument:
|
||||
filenames.add(key)
|
||||
else:
|
||||
discard
|
||||
if 0 == len(optstat):
|
||||
optstat.add("ototal")
|
||||
|
||||
if 0 == len(filenames):
|
||||
stderr.writeLine("usage: parselog [-nNN] [--stat ototal|rtotal|wtotal|dtotal|...] file...")
|
||||
quit(2)
|
||||
|
||||
for filename in filenames:
|
||||
parseLog filename, processReq, processRsp
|
||||
|
||||
for s in optstat:
|
||||
case s
|
||||
of "ototal":
|
||||
dumpstat ototal
|
||||
of "oerror":
|
||||
dumpstat oerror
|
||||
of "mtotal":
|
||||
dumpstat mtotal
|
||||
of "rtotal":
|
||||
dumpstat rtotal
|
||||
of "rbytes":
|
||||
dumpstat rbytes
|
||||
of "rerror":
|
||||
dumpstat rerror
|
||||
of "wtotal":
|
||||
dumpstat wtotal
|
||||
of "wbytes":
|
||||
dumpstat wbytes
|
||||
of "werror":
|
||||
dumpstat werror
|
||||
of "dtotal":
|
||||
dumpstat dtotal
|
||||
of "dbytes":
|
||||
dumpstat dbytes
|
||||
of "derror":
|
||||
dumpstat derror
|
||||
of "ptotal":
|
||||
dumpstat ptotal
|
||||
of "pbytes":
|
||||
dumpstat pbytes
|
||||
of "perror":
|
||||
dumpstat perror
|
||||
echo ""
|
||||
|
||||
when isMainModule:
|
||||
main()
|
Loading…
x
Reference in New Issue
Block a user