1
2
3 package logging
4
5 import (
6 "context"
7 "encoding/hex"
8 "fmt"
9 "io"
10 "strconv"
11 "strings"
12
13 "github.com/tetratelabs/wazero/api"
14 )
15
16
17
18 type ValueType = api.ValueType
19
20 const (
21 ValueTypeI32 = api.ValueTypeI32
22 ValueTypeI64 = api.ValueTypeI64
23 ValueTypeF32 = api.ValueTypeF32
24 ValueTypeF64 = api.ValueTypeF64
25 ValueTypeV128 ValueType = 0x7b
26 ValueTypeFuncref ValueType = 0x70
27 ValueTypeExternref = api.ValueTypeExternref
28
29
30 ValueTypeMemI32 = 0xfd
31
32 ValueTypeMemH64 = 0xfe
33
34 ValueTypeString = 0xff
35 )
36
37 type LogScopes uint64
38
39 const (
40 LogScopeNone = LogScopes(0)
41 LogScopeClock LogScopes = 1 << iota
42 LogScopeProc
43 LogScopeFilesystem
44 LogScopeMemory
45 LogScopePoll
46 LogScopeRandom
47 LogScopeSock
48 LogScopeAll = LogScopes(0xffffffffffffffff)
49 )
50
51 func scopeName(s LogScopes) string {
52 switch s {
53 case LogScopeClock:
54 return "clock"
55 case LogScopeProc:
56 return "proc"
57 case LogScopeFilesystem:
58 return "filesystem"
59 case LogScopeMemory:
60 return "memory"
61 case LogScopePoll:
62 return "poll"
63 case LogScopeRandom:
64 return "random"
65 case LogScopeSock:
66 return "sock"
67 default:
68 return fmt.Sprintf("<unknown=%d>", s)
69 }
70 }
71
72
73 func (f LogScopes) IsEnabled(scope LogScopes) bool {
74 return f&scope != 0
75 }
76
77
78 func (f LogScopes) String() string {
79 if f == LogScopeAll {
80 return "all"
81 }
82 var builder strings.Builder
83 for i := 0; i <= 63; i++ {
84 target := LogScopes(1 << i)
85 if f.IsEnabled(target) {
86 if name := scopeName(target); name != "" {
87 if builder.Len() > 0 {
88 builder.WriteByte('|')
89 }
90 builder.WriteString(name)
91 }
92 }
93 }
94 return builder.String()
95 }
96
97
98 type LoggerKey struct{}
99
100 type ParamLogger func(ctx context.Context, mod api.Module, w Writer, params []uint64)
101
102 type ParamSampler func(ctx context.Context, mod api.Module, params []uint64) bool
103
104 type ResultLogger func(ctx context.Context, mod api.Module, w Writer, params, results []uint64)
105
106 type Writer interface {
107 io.Writer
108 io.StringWriter
109 io.ByteWriter
110 }
111
112
113
114
115 type ValWriter func(ctx context.Context, mod api.Module, w Writer, i uint32, vals []uint64)
116
117 func Config(fnd api.FunctionDefinition) (paramLoggers []ParamLogger, resultLoggers []ResultLogger) {
118 types := fnd.ParamTypes()
119 names := fnd.ParamNames()
120 if paramLen := uint32(len(types)); paramLen > 0 {
121 paramLoggers = make([]ParamLogger, paramLen)
122 hasParamNames := len(names) > 0
123 for i, t := range types {
124 if hasParamNames {
125 paramLoggers[i] = NewParamLogger(uint32(i), names[i], t)
126 } else {
127 paramLoggers[i] = (¶mLogger{idx: uint32(i), valWriter: ValWriterForType(t)}).Log
128 }
129 }
130 }
131 if resultLen := uint32(len(fnd.ResultTypes())); resultLen > 0 {
132 resultLoggers = make([]ResultLogger, resultLen)
133 hasResultNames := len(fnd.ResultNames()) > 0
134 for i, t := range fnd.ResultTypes() {
135 if hasResultNames {
136 resultLoggers[i] = NewResultLogger(uint32(i), fnd.ResultNames()[i], t)
137 } else {
138 resultLoggers[i] = (&resultLogger{idx: uint32(i), valWriter: ValWriterForType(t)}).Log
139 }
140 }
141 }
142 return
143 }
144
145 type paramLogger struct {
146 idx uint32
147 valWriter ValWriter
148 }
149
150 func (n *paramLogger) Log(ctx context.Context, mod api.Module, w Writer, params []uint64) {
151 n.valWriter(ctx, mod, w, n.idx, params)
152 }
153
154 func NewParamLogger(idx uint32, name string, t ValueType) ParamLogger {
155 return (&namedParamLogger{idx: idx, name: name, valWriter: ValWriterForType(t)}).Log
156 }
157
158 type namedParamLogger struct {
159 idx uint32
160 name string
161 valWriter ValWriter
162 }
163
164 func (n *namedParamLogger) Log(ctx context.Context, mod api.Module, w Writer, params []uint64) {
165 w.WriteString(n.name)
166 w.WriteByte('=')
167 n.valWriter(ctx, mod, w, n.idx, params)
168 }
169
170 type resultLogger struct {
171 idx uint32
172 valWriter ValWriter
173 }
174
175 func (n *resultLogger) Log(ctx context.Context, mod api.Module, w Writer, _, results []uint64) {
176 n.valWriter(ctx, mod, w, n.idx, results)
177 }
178
179 func NewResultLogger(idx uint32, name string, t ValueType) ResultLogger {
180 return (&namedResultLogger{idx, name, ValWriterForType(t)}).Log
181 }
182
183 type namedResultLogger struct {
184 idx uint32
185 name string
186 valWriter ValWriter
187 }
188
189 func (n *namedResultLogger) Log(ctx context.Context, mod api.Module, w Writer, _, results []uint64) {
190 w.WriteString(n.name)
191 w.WriteByte('=')
192 n.valWriter(ctx, mod, w, n.idx, results)
193 }
194
195 func ValWriterForType(vt ValueType) ValWriter {
196 switch vt {
197 case ValueTypeI32:
198 return writeI32
199 case ValueTypeI64:
200 return writeI64
201 case ValueTypeF32:
202 return writeF32
203 case ValueTypeF64:
204 return writeF64
205 case ValueTypeV128:
206 return writeV128
207 case ValueTypeExternref, ValueTypeFuncref:
208 return writeRef
209 case ValueTypeMemI32:
210 return writeMemI32
211 case ValueTypeMemH64:
212 return writeMemH64
213 case ValueTypeString:
214 return writeString
215 default:
216 panic(fmt.Errorf("BUG: unsupported type %d", vt))
217 }
218 }
219
220 func writeI32(_ context.Context, _ api.Module, w Writer, i uint32, vals []uint64) {
221 v := vals[i]
222 w.WriteString(strconv.FormatInt(int64(int32(v)), 10))
223 }
224
225 func writeI64(_ context.Context, _ api.Module, w Writer, i uint32, vals []uint64) {
226 v := vals[i]
227 w.WriteString(strconv.FormatInt(int64(v), 10))
228 }
229
230 func writeF32(_ context.Context, _ api.Module, w Writer, i uint32, vals []uint64) {
231 v := vals[i]
232 s := strconv.FormatFloat(float64(api.DecodeF32(v)), 'g', -1, 32)
233 w.WriteString(s)
234 }
235
236 func writeF64(_ context.Context, _ api.Module, w Writer, i uint32, vals []uint64) {
237 v := vals[i]
238 s := strconv.FormatFloat(api.DecodeF64(v), 'g', -1, 64)
239 w.WriteString(s)
240 }
241
242
243 func writeV128(_ context.Context, _ api.Module, w Writer, i uint32, vals []uint64) {
244 v1, v2 := vals[i], vals[i+1]
245 w.WriteString(fmt.Sprintf("%016x%016x", v1, v2))
246 }
247
248
249 func writeRef(_ context.Context, _ api.Module, w Writer, i uint32, vals []uint64) {
250 v := vals[i]
251 w.WriteString(fmt.Sprintf("%016x", v))
252 }
253
254 func writeMemI32(_ context.Context, mod api.Module, w Writer, i uint32, vals []uint64) {
255 offset := uint32(vals[i])
256 byteCount := uint32(4)
257 if v, ok := mod.Memory().ReadUint32Le(offset); ok {
258 w.WriteString(strconv.FormatInt(int64(int32(v)), 10))
259 } else {
260 WriteOOM(w, offset, byteCount)
261 }
262 }
263
264 func writeMemH64(_ context.Context, mod api.Module, w Writer, i uint32, vals []uint64) {
265 offset := uint32(vals[i])
266 byteCount := uint32(8)
267 if s, ok := mod.Memory().Read(offset, byteCount); ok {
268 hex.NewEncoder(w).Write(s)
269 } else {
270 WriteOOM(w, offset, byteCount)
271 }
272 }
273
274 func writeString(_ context.Context, mod api.Module, w Writer, i uint32, vals []uint64) {
275 offset, byteCount := uint32(vals[i]), uint32(vals[i+1])
276 WriteStringOrOOM(mod.Memory(), w, offset, byteCount)
277 }
278
279 func WriteStringOrOOM(mem api.Memory, w Writer, offset, byteCount uint32) {
280 if s, ok := mem.Read(offset, byteCount); ok {
281 w.Write(s)
282 } else {
283 WriteOOM(w, offset, byteCount)
284 }
285 }
286
287 func WriteOOM(w Writer, offset uint32, byteCount uint32) {
288 w.WriteString("OOM(")
289 w.WriteString(strconv.Itoa(int(offset)))
290 w.WriteByte(',')
291 w.WriteString(strconv.Itoa(int(byteCount)))
292 w.WriteByte(')')
293 }
294
View as plain text