aea3c68c by astaxie

add debug print func! fix #278

1 parent 8368360a
1 // most reference from github.com/realint/dbgutil
2 package toolbox
3
4 import (
5 "bytes"
6 "fmt"
7 "log"
8 "reflect"
9 "runtime"
10 )
11
12 var (
13 dunno = []byte("???")
14 centerDot = []byte("·")
15 dot = []byte(".")
16 lbr = []byte("{")
17 lbrn = []byte("{\n")
18 com = []byte(",")
19 comn = []byte(",\n")
20 rbr = []byte("}")
21 comnrbr = []byte(",\n}")
22 )
23
24 type pointerInfo struct {
25 prev *pointerInfo
26 n int
27 addr uintptr
28 pos int
29 used []int
30 }
31
32 //
33 // print the data in console
34 //
35 func Display(data ...interface{}) {
36 display(true, data...)
37 }
38
39 //
40 // return string
41 //
42 func GetDisplayString(data ...interface{}) string {
43 return display(false, data...)
44 }
45
46 func display(displayed bool, data ...interface{}) string {
47 var pc, file, line, ok = runtime.Caller(2)
48
49 if !ok {
50 return ""
51 }
52
53 var buf = new(bytes.Buffer)
54
55 fmt.Fprintf(buf, "[Debug] at %s() [%s:%d]\n", function(pc), file, line)
56
57 fmt.Fprintf(buf, "\n[Variables]\n")
58
59 for i := 0; i < len(data); i += 2 {
60 var output = fomateinfo(len(data[i].(string))+3, data[i+1])
61 fmt.Fprintf(buf, "%s = %s", data[i], output)
62 }
63
64 if displayed {
65 log.Print(buf)
66 }
67 return buf.String()
68 }
69
70 //
71 // return fomateinfo
72 //
73 func fomateinfo(headlen int, data ...interface{}) []byte {
74 var buf = new(bytes.Buffer)
75
76 if len(data) > 1 {
77 fmt.Fprint(buf, " ")
78
79 fmt.Fprint(buf, "[")
80
81 fmt.Fprintln(buf)
82 }
83
84 for k, v := range data {
85 var buf2 = new(bytes.Buffer)
86 var pointers *pointerInfo
87 var interfaces []reflect.Value = make([]reflect.Value, 0, 10)
88
89 printKeyValue(buf2, reflect.ValueOf(v), &pointers, &interfaces, nil, true, " ", 1)
90
91 if k < len(data)-1 {
92 fmt.Fprint(buf2, ", ")
93 }
94
95 fmt.Fprintln(buf2)
96
97 buf.Write(buf2.Bytes())
98 }
99
100 if len(data) > 1 {
101 fmt.Fprintln(buf)
102
103 fmt.Fprint(buf, " ")
104
105 fmt.Fprint(buf, "]")
106 }
107
108 return buf.Bytes()
109 }
110
111 func isSimpleType(val reflect.Value, kind reflect.Kind, pointers **pointerInfo, interfaces *[]reflect.Value) bool {
112 switch kind {
113 case reflect.Bool:
114 return true
115 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
116 return true
117 case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
118 return true
119 case reflect.Float32, reflect.Float64:
120 return true
121 case reflect.Complex64, reflect.Complex128:
122 return true
123 case reflect.String:
124 return true
125 case reflect.Chan:
126 return true
127 case reflect.Invalid:
128 return true
129 case reflect.Interface:
130 for _, in := range *interfaces {
131 if reflect.DeepEqual(in, val) {
132 return true
133 }
134 }
135 return false
136 case reflect.UnsafePointer:
137 if val.IsNil() {
138 return true
139 }
140
141 var elem = val.Elem()
142
143 if isSimpleType(elem, elem.Kind(), pointers, interfaces) {
144 return true
145 }
146
147 var addr = val.Elem().UnsafeAddr()
148
149 for p := *pointers; p != nil; p = p.prev {
150 if addr == p.addr {
151 return true
152 }
153 }
154
155 return false
156 }
157
158 return false
159 }
160
161 func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, interfaces *[]reflect.Value, structFilter func(string, string) bool, formatOutput bool, indent string, level int) {
162 var t = val.Kind()
163
164 switch t {
165 case reflect.Bool:
166 fmt.Fprint(buf, val.Bool())
167 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
168 fmt.Fprint(buf, val.Int())
169 case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
170 fmt.Fprint(buf, val.Uint())
171 case reflect.Float32, reflect.Float64:
172 fmt.Fprint(buf, val.Float())
173 case reflect.Complex64, reflect.Complex128:
174 fmt.Fprint(buf, val.Complex())
175 case reflect.UnsafePointer:
176 fmt.Fprintf(buf, "unsafe.Pointer(0x%X)", val.Pointer())
177 case reflect.Ptr:
178 if val.IsNil() {
179 fmt.Fprint(buf, "nil")
180 return
181 }
182
183 var addr = val.Elem().UnsafeAddr()
184
185 for p := *pointers; p != nil; p = p.prev {
186 if addr == p.addr {
187 p.used = append(p.used, buf.Len())
188 fmt.Fprintf(buf, "0x%X", addr)
189 return
190 }
191 }
192
193 *pointers = &pointerInfo{
194 prev: *pointers,
195 addr: addr,
196 pos: buf.Len(),
197 used: make([]int, 0),
198 }
199
200 fmt.Fprint(buf, "&")
201
202 printKeyValue(buf, val.Elem(), pointers, interfaces, structFilter, formatOutput, indent, level)
203 case reflect.String:
204 fmt.Fprint(buf, "\"", val.String(), "\"")
205 case reflect.Interface:
206 var value = val.Elem()
207
208 if !value.IsValid() {
209 fmt.Fprint(buf, "nil")
210 } else {
211 for _, in := range *interfaces {
212 if reflect.DeepEqual(in, val) {
213 fmt.Fprint(buf, "repeat")
214 return
215 }
216 }
217
218 *interfaces = append(*interfaces, val)
219
220 printKeyValue(buf, value, pointers, interfaces, structFilter, formatOutput, indent, level+1)
221 }
222 case reflect.Struct:
223 var t = val.Type()
224
225 fmt.Fprint(buf, t)
226 fmt.Fprint(buf, "{")
227
228 for i := 0; i < val.NumField(); i++ {
229 if formatOutput {
230 fmt.Fprintln(buf)
231 } else {
232 fmt.Fprint(buf, " ")
233 }
234
235 var name = t.Field(i).Name
236
237 if formatOutput {
238 for ind := 0; ind < level; ind++ {
239 fmt.Fprint(buf, indent)
240 }
241 }
242
243 fmt.Fprint(buf, name)
244 fmt.Fprint(buf, ": ")
245
246 if structFilter != nil && structFilter(t.String(), name) {
247 fmt.Fprint(buf, "ignore")
248 } else {
249 printKeyValue(buf, val.Field(i), pointers, interfaces, structFilter, formatOutput, indent, level+1)
250 }
251
252 fmt.Fprint(buf, ",")
253 }
254
255 if formatOutput {
256 fmt.Fprintln(buf)
257
258 for ind := 0; ind < level-1; ind++ {
259 fmt.Fprint(buf, indent)
260 }
261 } else {
262 fmt.Fprint(buf, " ")
263 }
264
265 fmt.Fprint(buf, "}")
266 case reflect.Array, reflect.Slice:
267 fmt.Fprint(buf, val.Type())
268 fmt.Fprint(buf, "{")
269
270 var allSimple = true
271
272 for i := 0; i < val.Len(); i++ {
273 var elem = val.Index(i)
274
275 var isSimple = isSimpleType(elem, elem.Kind(), pointers, interfaces)
276
277 if !isSimple {
278 allSimple = false
279 }
280
281 if formatOutput && !isSimple {
282 fmt.Fprintln(buf)
283 } else {
284 fmt.Fprint(buf, " ")
285 }
286
287 if formatOutput && !isSimple {
288 for ind := 0; ind < level; ind++ {
289 fmt.Fprint(buf, indent)
290 }
291 }
292
293 printKeyValue(buf, elem, pointers, interfaces, structFilter, formatOutput, indent, level+1)
294
295 if i != val.Len()-1 || !allSimple {
296 fmt.Fprint(buf, ",")
297 }
298 }
299
300 if formatOutput && !allSimple {
301 fmt.Fprintln(buf)
302
303 for ind := 0; ind < level-1; ind++ {
304 fmt.Fprint(buf, indent)
305 }
306 } else {
307 fmt.Fprint(buf, " ")
308 }
309
310 fmt.Fprint(buf, "}")
311 case reflect.Map:
312 var t = val.Type()
313 var keys = val.MapKeys()
314
315 fmt.Fprint(buf, t)
316 fmt.Fprint(buf, "{")
317
318 var allSimple = true
319
320 for i := 0; i < len(keys); i++ {
321 var elem = val.MapIndex(keys[i])
322
323 var isSimple = isSimpleType(elem, elem.Kind(), pointers, interfaces)
324
325 if !isSimple {
326 allSimple = false
327 }
328
329 if formatOutput && !isSimple {
330 fmt.Fprintln(buf)
331 } else {
332 fmt.Fprint(buf, " ")
333 }
334
335 if formatOutput && !isSimple {
336 for ind := 0; ind <= level; ind++ {
337 fmt.Fprint(buf, indent)
338 }
339 }
340
341 printKeyValue(buf, keys[i], pointers, interfaces, structFilter, formatOutput, indent, level+1)
342 fmt.Fprint(buf, ": ")
343 printKeyValue(buf, elem, pointers, interfaces, structFilter, formatOutput, indent, level+1)
344
345 if i != val.Len()-1 || !allSimple {
346 fmt.Fprint(buf, ",")
347 }
348 }
349
350 if formatOutput && !allSimple {
351 fmt.Fprintln(buf)
352
353 for ind := 0; ind < level-1; ind++ {
354 fmt.Fprint(buf, indent)
355 }
356 } else {
357 fmt.Fprint(buf, " ")
358 }
359
360 fmt.Fprint(buf, "}")
361 case reflect.Chan:
362 fmt.Fprint(buf, val.Type())
363 case reflect.Invalid:
364 fmt.Fprint(buf, "invalid")
365 default:
366 fmt.Fprint(buf, "unknow")
367 }
368 }
369
370 func printPointerInfo(buf *bytes.Buffer, headlen int, pointers *pointerInfo) {
371 var anyused = false
372 var pointerNum = 0
373
374 for p := pointers; p != nil; p = p.prev {
375 if len(p.used) > 0 {
376 anyused = true
377 }
378 pointerNum += 1
379 p.n = pointerNum
380 }
381
382 if anyused {
383 var pointerBufs = make([][]rune, pointerNum+1)
384
385 for i := 0; i < len(pointerBufs); i++ {
386 var pointerBuf = make([]rune, buf.Len()+headlen)
387
388 for j := 0; j < len(pointerBuf); j++ {
389 pointerBuf[j] = ' '
390 }
391
392 pointerBufs[i] = pointerBuf
393 }
394
395 for pn := 0; pn <= pointerNum; pn++ {
396 for p := pointers; p != nil; p = p.prev {
397 if len(p.used) > 0 && p.n >= pn {
398 if pn == p.n {
399 pointerBufs[pn][p.pos+headlen] = '└'
400
401 var maxpos = 0
402
403 for i, pos := range p.used {
404 if i < len(p.used)-1 {
405 pointerBufs[pn][pos+headlen] = '┴'
406 } else {
407 pointerBufs[pn][pos+headlen] = '┘'
408 }
409
410 maxpos = pos
411 }
412
413 for i := 0; i < maxpos-p.pos-1; i++ {
414 if pointerBufs[pn][i+p.pos+headlen+1] == ' ' {
415 pointerBufs[pn][i+p.pos+headlen+1] = '─'
416 }
417 }
418 } else {
419 pointerBufs[pn][p.pos+headlen] = '│'
420
421 for _, pos := range p.used {
422 if pointerBufs[pn][pos+headlen] == ' ' {
423 pointerBufs[pn][pos+headlen] = '│'
424 } else {
425 pointerBufs[pn][pos+headlen] = '┼'
426 }
427 }
428 }
429 }
430 }
431
432 buf.WriteString(string(pointerBufs[pn]) + "\n")
433 }
434 }
435 }
436
437 //
438 // get stack info
439 //
440 func stack(skip int, indent string) []byte {
441 var buf = new(bytes.Buffer)
442
443 for i := skip; ; i++ {
444 var pc, file, line, ok = runtime.Caller(i)
445
446 if !ok {
447 break
448 }
449
450 buf.WriteString(indent)
451
452 fmt.Fprintf(buf, "at %s() [%s:%d]\n", function(pc), file, line)
453 }
454
455 return buf.Bytes()
456 }
457
458 // function returns, if possible, the name of the function containing the PC.
459 func function(pc uintptr) []byte {
460 fn := runtime.FuncForPC(pc)
461 if fn == nil {
462 return dunno
463 }
464 name := []byte(fn.Name())
465 // The name includes the path name to the package, which is unnecessary
466 // since the file name is already included. Plus, it has center dots.
467 // That is, we see
468 // runtime/debug.*T·ptrmethod
469 // and want
470 // *T.ptrmethod
471 if period := bytes.Index(name, dot); period >= 0 {
472 name = name[period+1:]
473 }
474 name = bytes.Replace(name, centerDot, dot, -1)
475 return name
476 }
1 package toolbox
2
3 import (
4 "testing"
5 )
6
7 type mytype struct {
8 next *mytype
9 prev *mytype
10 }
11
12 func TestPrint(t *testing.T) {
13 Display("v1", 1, "v2", 2, "v3", 3)
14 }
15
16 func TestPrintPoint(t *testing.T) {
17 var v1 = new(mytype)
18 var v2 = new(mytype)
19
20 v1.prev = nil
21 v1.next = v2
22
23 v2.prev = v1
24 v2.next = nil
25
26 Display("v1", v1, "v2", v2)
27 }
28
29 func TestPrintString(t *testing.T) {
30 str := GetDisplayString("v1", 1, "v2", 2)
31 println(str)
32 }
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!