add debug print func! fix #278
Showing
2 changed files
with
508 additions
and
0 deletions
toolbox/debug.go
0 → 100644
| 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 | } |
toolbox/debug_test.go
0 → 100644
| 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 | } |
-
Please register or sign in to post a comment