Skip to content

Commit ed7a254

Browse files
committed
feat(Lib): sys: audit, addaudithook
1 parent 86698e4 commit ed7a254

File tree

6 files changed

+156
-1
lines changed

6 files changed

+156
-1
lines changed

src/pylib/Lib/n_sys.nim

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ impExp sys_impl,
1616
stdio,
1717
exits,
1818
getencodings,
19-
sizes
19+
sizes,
20+
auditImpl
2021
2122

src/pylib/Lib/sys_impl/auditImpl.nim

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## When `not defined(release) or defined(pylibSysAudit)`, audit will be enabled.
2+
## Otherwise, it will be disabled.
3+
import ./auditImpl/main
4+
export main
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
3+
const config_should_audit =
4+
not defined(release) or defined(pylibSysAudit)
5+
6+
when config_should_audit:
7+
include ./yesImpl
8+
else:
9+
include ./noImpl
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
import ./types
3+
4+
template PySys_Audit*(event: string, args: varargs[typed]) = discard
5+
6+
template reportNoAudit() =
7+
{.warning: "audit not enabled, enable via `-d:pylibSysAudit` in release build mode".}
8+
9+
template PySys_AddAuditHook*(hook: HookProc, userData = default Any) = bind reportNoAudit; reportNoAudit
10+
template addaudithook*(hook: HookProc) = bind reportNoAudit; reportNoAudit
11+
12+
template audit*(event: string, args: varargs[typed]) = discard
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
import std/typeinfo
3+
export typeinfo
4+
5+
type
6+
HookProc* = proc (event: string; args: varargs[Any])
7+
HookEntry* = tuple[
8+
hookCFunction: HookProc,
9+
userData: Any,
10+
]
11+
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
2+
import ./types
3+
import ../../../pyerrors/rterr
4+
import std/lists
5+
import std/locks
6+
import std/macros
7+
8+
template mytoAny[T](x: T): Any =
9+
bind toAny
10+
var tx = x
11+
toAny(tx)
12+
13+
proc nodeToAny(n: NimNode): NimNode = newCall(bindSym"mytoAny", n)
14+
15+
proc callVarargImpl(call, arg1, vargs: NimNode): NimNode =
16+
# NIM-BUG: vargs will become Sym of nil when doc
17+
expectKind vargs, nnkBracket
18+
result = newCall(call, arg1)
19+
for i in vargs:
20+
result.add i.nodeToAny
21+
22+
macro callVararg(call, arg1, vargs: typed) =
23+
callVarargImpl(call, arg1, vargs)
24+
25+
macro callWithExtraVararg(call, arg1, vargs: typed, extra: Any) =
26+
result = callVarargImpl(call, arg1, vargs)
27+
result.add extra
28+
29+
template initHookList(): SinglyLinkedList[HookEntry] = initSinglyLinkedList[HookEntry]()
30+
template toHookEntry(v: HookProc, userData: Any): HookEntry = (v, userData)
31+
32+
type
33+
PyRuntimeState = object
34+
audit_hooks*: tuple[
35+
head: SinglyLinkedList[HookEntry],
36+
mutex: Lock,
37+
]
38+
39+
var runtime = PyRuntimeState(
40+
audit_hooks: (
41+
head: initHookList(),
42+
mutex: Lock(),
43+
)
44+
)
45+
runtime.audit_hooks.mutex.initLock()
46+
47+
var interp = (
48+
audit_hooks: newSeq[HookProc]()
49+
)
50+
proc PyDTrace_AUDIT_ENABLED(): bool = false ## Currently not implemented
51+
proc should_audit(): bool{.inline.} =
52+
not runtime.audit_hooks.head.head.isNil or
53+
interp.audit_hooks.len > 0 or
54+
PyDTrace_AUDIT_ENABLED()
55+
56+
template PySys_AuditImpl(event: string, args: untyped) =
57+
## `PySys_Audit`/`sys_audit_tstate` EXT. CPython C-API
58+
bind runtime, callWithExtraVararg, callVararg, items
59+
for e in runtime.audit_hooks.head.items:
60+
callWithExtraVararg(
61+
e.hookCFunction,
62+
event, args, e.userData)
63+
64+
#[if dtrace: ...]#
65+
66+
# Call interpreter hooks
67+
for hook in interp.audit_hooks:
68+
callVararg(hook, event, args)
69+
70+
template PySys_Audit*(event: string, args: varargs[typed]) =
71+
bind PySys_AuditImpl
72+
PySys_AuditImpl(event, args)
73+
74+
proc add_audit_hook_entry_unlocked(runtime: var PyRuntimeState, entry: HookEntry) =
75+
runtime.audit_hooks.head.append entry
76+
77+
template auditAddAuditHook(exc){.dirty.} =
78+
## `PySys_Audit` EXT. CPython C-API
79+
try:
80+
PySys_Audit("sys.addaudithook")
81+
except exc:
82+
# We do not report errors derived from exc
83+
discard
84+
85+
proc PySys_AddAuditHook*(hook: HookProc, userData = default Any) =
86+
## `PySys_Audit` EXT. CPython C-API
87+
auditAddAuditHook RuntimeError
88+
89+
withLock runtime.audit_hooks.mutex:
90+
add_audit_hook_entry_unlocked(runtime, hook.toHookEntry userData)
91+
92+
93+
proc addaudithook*(hook: HookProc) =
94+
auditAddAuditHook CatchableError
95+
interp.audit_hooks.add hook
96+
97+
template audit*(event: string, args: varargs[typed]) =
98+
bind should_audit, PySys_AuditImpl
99+
if should_audit():
100+
PySys_AuditImpl(event, args)
101+
102+
const ClearAuditHooksName = "cpython._PySys_ClearAuditHooks"
103+
proc PySys_ClearAuditHooks() =
104+
# TODO: after config.verbose
105+
#if config.verbose: PySys_WriteStderr("# clear sys.audit hooks\n")
106+
try: PySys_Audit(ClearAuditHooksName)
107+
except Exception: discard
108+
109+
when (NimMajor, NimMinor, NimPatch) >= (2, 1, 1):
110+
## XXX: FIXED-NIM-BUG: though nimAllowNonVarDestructor is defined at least since 2.0.6,
111+
## it still cannot be compiled till abour 2.1.1
112+
proc `=destroy`*(self: PyRuntimeState) = PySys_ClearAuditHooks()
113+
else:
114+
proc `=destroy`*(self: var PyRuntimeState) = PySys_ClearAuditHooks()
115+
116+
when isMainModule:
117+
addaudithook do (event: string, _: varargs[Any]):
118+
assert event == ClearAuditHooksName

0 commit comments

Comments
 (0)