github.com/ks888/tgo@v0.0.0-20190130135156-80bf89407292/tracee/process_test.go (about) 1 package tracee 2 3 import ( 4 "debug/dwarf" 5 "os/exec" 6 "runtime" 7 "testing" 8 9 "github.com/ks888/tgo/testutils" 10 "golang.org/x/arch/x86/x86asm" 11 ) 12 13 var helloworldAttr = Attributes{ 14 FirstModuleDataAddr: testutils.HelloworldAddrFirstModuleData, 15 CompiledGoVersion: runtime.Version(), 16 } 17 18 var infloopAttr = Attributes{ 19 ProgramPath: testutils.ProgramInfloop, 20 FirstModuleDataAddr: testutils.InfloopAddrFirstModuleData, 21 CompiledGoVersion: runtime.Version(), 22 } 23 24 func TestLaunchProcess(t *testing.T) { 25 proc, err := LaunchProcess(testutils.ProgramHelloworld, nil, helloworldAttr) 26 if err != nil { 27 t.Fatalf("failed to launch process: %v", err) 28 } 29 defer proc.Detach() 30 if proc.debugapiClient == nil { 31 t.Errorf("debugapiClient is nil") 32 } 33 } 34 35 func TestAttachProcess(t *testing.T) { 36 cmd := exec.Command(testutils.ProgramInfloop) 37 _ = cmd.Start() 38 39 proc, err := AttachProcess(cmd.Process.Pid, infloopAttr) 40 if err != nil { 41 t.Fatalf("failed to attach process: %v", err) 42 } 43 if proc.debugapiClient == nil { 44 t.Errorf("debugapiClient is nil") 45 } 46 defer func() { 47 proc.Detach() // must detach before kill. Otherwise, the program becomes zombie. 48 cmd.Process.Kill() 49 cmd.Process.Wait() 50 }() 51 } 52 53 func TestDetach(t *testing.T) { 54 proc, err := LaunchProcess(testutils.ProgramHelloworld, nil, helloworldAttr) 55 if err != nil { 56 t.Fatalf("failed to launch process: %v", err) 57 } 58 59 if err := proc.SetBreakpoint(testutils.HelloworldAddrNoParameter); err != nil { 60 t.Fatalf("failed to set breakpoint: %v", err) 61 } 62 63 if err := proc.Detach(); err != nil { 64 t.Fatalf("failed to detach process: %v", err) 65 } 66 67 if proc.ExistBreakpoint(testutils.HelloworldAddrNoParameter) { 68 t.Errorf("breakpoint still exists") 69 } 70 } 71 72 func TestContinueAndWait(t *testing.T) { 73 proc, err := LaunchProcess(testutils.ProgramHelloworld, nil, helloworldAttr) 74 if err != nil { 75 t.Fatalf("failed to launch process: %v", err) 76 } 77 defer proc.Detach() 78 79 // 1. stop at NoParameter func 80 if err := proc.SetBreakpoint(testutils.HelloworldAddrNoParameter); err != nil { 81 t.Fatalf("failed to set breakpoint: %v", err) 82 } 83 event, err := proc.ContinueAndWait() 84 if err != nil { 85 t.Fatalf("failed to continue and wait: %v", err) 86 } 87 if err := proc.ClearBreakpoint(testutils.HelloworldAddrNoParameter); err != nil { 88 t.Fatalf("failed to set breakpoint: %v", err) 89 } 90 tids := event.Data.([]int) 91 if err := proc.setPC(tids[0], testutils.HelloworldAddrNoParameter); err != nil { 92 t.Fatalf("failed to set breakpoint: %v", err) 93 } 94 95 // 2. stop at OneParameter func 96 if err := proc.SetBreakpoint(testutils.HelloworldAddrOneParameter); err != nil { 97 t.Fatalf("failed to set breakpoint: %v", err) 98 } 99 if _, err := proc.ContinueAndWait(); err != nil { 100 t.Fatalf("failed to continue and wait: %v", err) 101 } 102 info, err := proc.CurrentGoRoutineInfo(tids[0]) 103 if err != nil { 104 t.Fatalf("failed to get CurrentGoRoutineInfo: %v", err) 105 } 106 if info.CurrentPC-1 != testutils.HelloworldAddrOneParameter { 107 t.Errorf("stop at unexpected address: %x", info.CurrentPC) 108 } 109 } 110 111 func TestSingleStep(t *testing.T) { 112 proc, err := LaunchProcess(testutils.ProgramHelloworld, nil, helloworldAttr) 113 if err != nil { 114 t.Fatalf("failed to launch process: %v", err) 115 } 116 defer proc.Detach() 117 118 if err := proc.SetBreakpoint(testutils.HelloworldAddrNoParameter); err != nil { 119 t.Fatalf("failed to set breakpoint: %v", err) 120 } 121 event, err := proc.ContinueAndWait() 122 if err != nil { 123 t.Fatalf("failed to continue and wait: %v", err) 124 } 125 126 tids := event.Data.([]int) 127 if err := proc.SingleStep(tids[0], testutils.HelloworldAddrNoParameter); err != nil { 128 t.Fatalf("single-step failed: %v", err) 129 } 130 if !proc.ExistBreakpoint(testutils.HelloworldAddrNoParameter) { 131 t.Errorf("breakpoint is cleared") 132 } 133 } 134 135 func TestSingleStep_NoBreakpoint(t *testing.T) { 136 proc, err := LaunchProcess(testutils.ProgramHelloworld, nil, helloworldAttr) 137 if err != nil { 138 t.Fatalf("failed to launch process: %v", err) 139 } 140 defer proc.Detach() 141 142 if err := proc.SetBreakpoint(testutils.HelloworldAddrNoParameter); err != nil { 143 t.Fatalf("failed to set breakpoint: %v", err) 144 } 145 event, err := proc.ContinueAndWait() 146 if err != nil { 147 t.Fatalf("failed to continue and wait: %v", err) 148 } 149 if err := proc.ClearBreakpoint(testutils.HelloworldAddrNoParameter); err != nil { 150 t.Fatalf("failed to clear breakpoint: %v", err) 151 } 152 153 tids := event.Data.([]int) 154 if err := proc.SingleStep(tids[0], testutils.HelloworldAddrNoParameter); err != nil { 155 t.Fatalf("single-step failed: %v", err) 156 } 157 if proc.ExistBreakpoint(testutils.HelloworldAddrNoParameter) { 158 t.Errorf("breakpoint is set") 159 } 160 } 161 162 func TestStackFrameAt(t *testing.T) { 163 proc, err := LaunchProcess(testutils.ProgramHelloworld, nil, helloworldAttr) 164 if err != nil { 165 t.Fatalf("failed to launch process: %v", err) 166 } 167 defer proc.Detach() 168 169 if err := proc.SetBreakpoint(testutils.HelloworldAddrOneParameterAndVariable); err != nil { 170 t.Fatalf("failed to set breakpoint: %v", err) 171 } 172 173 event, err := proc.ContinueAndWait() 174 if err != nil { 175 t.Fatalf("failed to continue and wait: %v", err) 176 } 177 178 tids := event.Data.([]int) 179 regs, err := proc.debugapiClient.ReadRegisters(tids[0]) 180 if err != nil { 181 t.Fatalf("failed to read registers: %v", err) 182 } 183 184 stackFrame, err := proc.StackFrameAt(regs.Rsp, regs.Rip) 185 if err != nil { 186 t.Fatalf("error: %v", err) 187 } 188 if stackFrame.Function.Name != "main.oneParameterAndOneVariable" { 189 t.Errorf("wrong function name: %s", stackFrame.Function.Name) 190 } 191 if stackFrame.Function.StartAddr != testutils.HelloworldAddrOneParameterAndVariable { 192 t.Errorf("start addr is 0") 193 } 194 if stackFrame.Function.EndAddr == 0 { 195 t.Errorf("end addr is 0") 196 } 197 if stackFrame.ReturnAddress == 0x0 { 198 t.Errorf("empty return address") 199 } 200 if len(stackFrame.InputArguments) != 1 { 201 t.Errorf("wrong input args length: %d", len(stackFrame.InputArguments)) 202 } 203 if stackFrame.InputArguments[0].ParseValue(1) != "i = 1" { 204 t.Errorf("wrong input args: %s", stackFrame.InputArguments[0].ParseValue(1)) 205 } 206 if len(stackFrame.OutputArguments) != 0 { 207 t.Errorf("wrong output args length: %d", len(stackFrame.OutputArguments)) 208 } 209 } 210 211 func TestStackFrameAt_NoDwarfCase(t *testing.T) { 212 proc, err := LaunchProcess(testutils.ProgramHelloworldNoDwarf, nil, helloworldAttr) 213 if err != nil { 214 t.Fatalf("failed to launch process: %v", err) 215 } 216 defer proc.Detach() 217 218 if err := proc.SetBreakpoint(testutils.HelloworldAddrOneParameterAndVariable); err != nil { 219 t.Fatalf("failed to set breakpoint: %v", err) 220 } 221 222 event, err := proc.ContinueAndWait() 223 if err != nil { 224 t.Fatalf("failed to continue and wait: %v", err) 225 } 226 227 tids := event.Data.([]int) 228 regs, err := proc.debugapiClient.ReadRegisters(tids[0]) 229 if err != nil { 230 t.Fatalf("failed to read registers: %v", err) 231 } 232 233 stackFrame, err := proc.StackFrameAt(regs.Rsp, regs.Rip) 234 if err != nil { 235 t.Fatalf("error: %v", err) 236 } 237 if stackFrame.Function.Name != "main.oneParameterAndOneVariable" { 238 t.Errorf("wrong function name: %s", stackFrame.Function.Name) 239 } 240 if stackFrame.Function.StartAddr != testutils.HelloworldAddrOneParameterAndVariable { 241 t.Errorf("wrong function value: %#x", stackFrame.Function.StartAddr) 242 } 243 if stackFrame.Function.EndAddr == 0 { 244 t.Errorf("end addr is 0") 245 } 246 if len(stackFrame.Function.Parameters) != 1 { 247 t.Errorf("wrong number of params") 248 } 249 if stackFrame.Function.Parameters[0].IsOutput { 250 t.Errorf("should be input parameter") 251 } 252 } 253 254 func TestFindFunction_FillInOneUnknownParameterOffset(t *testing.T) { 255 for i, testdata := range []uint64{ 256 testutils.HelloworldAddrOneParameter, 257 testutils.HelloworldAddrErrorsNew, 258 } { 259 proc, err := LaunchProcess(testutils.ProgramHelloworld, nil, helloworldAttr) 260 if err != nil { 261 t.Fatalf("failed to launch process: %v", err) 262 } 263 defer proc.Detach() 264 265 if err := proc.SetBreakpoint(testutils.HelloworldAddrMain); err != nil { 266 t.Fatalf("failed to set breakpoint: %v", err) 267 } 268 269 if _, err := proc.ContinueAndWait(); err != nil { 270 t.Fatalf("failed to continue and wait: %v", err) 271 } 272 273 f, err := proc.FindFunction(testdata) 274 if err != nil { 275 t.Fatalf("[%d] failed to find func for %x: %v", i, testdata, err) 276 } 277 278 numNotExist := 0 279 numOffset0 := 0 280 for _, param := range f.Parameters { 281 if !param.Exist { 282 numNotExist++ 283 } 284 if param.Offset == 0 { 285 numOffset0++ 286 } 287 } 288 if numNotExist == 1 { 289 t.Errorf("The number of NonExist parameter is 1, params: %#v", f.Parameters) 290 } 291 if numOffset0 != 1 { 292 t.Errorf("The number of offset 0 parameter is %d, params: %#v", numOffset0, f.Parameters) 293 } 294 } 295 } 296 297 func TestFindFunction_FillInOutputParametersOffset(t *testing.T) { 298 proc, err := LaunchProcess(testutils.ProgramHelloworld, nil, helloworldAttr) 299 if err != nil { 300 t.Fatalf("failed to launch process: %v", err) 301 } 302 defer proc.Detach() 303 304 if err := proc.SetBreakpoint(testutils.HelloworldAddrMain); err != nil { 305 t.Fatalf("failed to set breakpoint: %v", err) 306 } 307 308 if _, err := proc.ContinueAndWait(); err != nil { 309 t.Fatalf("failed to continue and wait: %v", err) 310 } 311 312 f, err := proc.FindFunction(testutils.HelloworldAddrTwoReturns) 313 if err != nil { 314 t.Fatalf("failed to find func: %v", err) 315 } 316 317 if !f.Parameters[0].Exist || f.Parameters[0].Offset != 0 || f.Parameters[0].Name != "~r0" { 318 t.Errorf("Invalid parameter: %#v", f.Parameters[0]) 319 } 320 if !f.Parameters[1].Exist || f.Parameters[1].Offset != 8 || f.Parameters[1].Name != "~r1" { 321 t.Errorf("Invalid parameter: %#v", f.Parameters[1]) 322 } 323 } 324 325 func TestFuncTypeOffsets(t *testing.T) { 326 binary, _ := OpenBinaryFile(testutils.ProgramHelloworld, GoVersion{}) 327 debuggableBinary, _ := binary.(debuggableBinaryFile) 328 329 entry, err := debuggableBinary.findDWARFEntryByName(func(entry *dwarf.Entry) bool { 330 if entry.Tag != dwarf.TagStructType { 331 return false 332 } 333 name, err := stringClassAttr(entry, dwarf.AttrName) 334 return name == "runtime._func" && err == nil 335 }) 336 if err != nil { 337 t.Fatalf("no _func type entry: %v", err) 338 } 339 340 expectedFuncType, err := debuggableBinary.dwarf.Type(entry.Offset) 341 if err != nil { 342 t.Fatalf("no func type: %v", err) 343 } 344 345 expectedFields := expectedFuncType.(*dwarf.StructType).Field 346 for _, actualField := range _funcType.Field { 347 for _, expectedField := range expectedFields { 348 if actualField.Name == expectedField.Name { 349 if actualField.ByteOffset != expectedField.ByteOffset { 350 t.Errorf("wrong byte offset. expect: %d, actual: %d", expectedField.ByteOffset, actualField.ByteOffset) 351 } 352 if actualField.Type.Size() != expectedField.Type.Size() { 353 t.Errorf("wrong size. expect: %d, actual: %d", expectedField.Type.Size(), actualField.Type.Size()) 354 } 355 break 356 } 357 } 358 } 359 } 360 361 func TestFindfuncbucketTypeOffsets(t *testing.T) { 362 if !ParseGoVersion(runtime.Version()).LaterThan(GoVersion{MajorVersion: 1, MinorVersion: 11}) { 363 t.Skip("go1.10 or earlier doesn't have findfuncbucket type in DWARF") 364 } 365 366 binary, _ := OpenBinaryFile(testutils.ProgramHelloworld, GoVersion{}) 367 debuggableBinary, _ := binary.(debuggableBinaryFile) 368 369 entry, err := debuggableBinary.findDWARFEntryByName(func(entry *dwarf.Entry) bool { 370 if entry.Tag != dwarf.TagStructType { 371 return false 372 } 373 name, err := stringClassAttr(entry, dwarf.AttrName) 374 return name == "runtime.findfuncbucket" && err == nil 375 }) 376 if err != nil { 377 t.Fatalf("no findfuncbucket type entry: %v", err) 378 } 379 380 expectedFindfuncbucketType, err := debuggableBinary.dwarf.Type(entry.Offset) 381 if err != nil { 382 t.Fatalf("no findfuncbucket type: %v", err) 383 } 384 385 expectedFields := expectedFindfuncbucketType.(*dwarf.StructType).Field 386 for _, actualField := range findfuncbucketType.Field { 387 for _, expectedField := range expectedFields { 388 if actualField.Name == expectedField.Name { 389 if actualField.ByteOffset != expectedField.ByteOffset { 390 t.Errorf("wrong byte offset. expect: %d, actual: %d", expectedField.ByteOffset, actualField.ByteOffset) 391 } 392 if actualField.Type.Size() != expectedField.Type.Size() { 393 t.Errorf("wrong size. expect: %d, actual: %d", expectedField.Type.Size(), actualField.Type.Size()) 394 } 395 break 396 } 397 } 398 } 399 } 400 401 func TestReadInstructions(t *testing.T) { 402 for _, testdata := range []struct { 403 program string 404 funcAddr uint64 405 targetOS string // specify if the func is not available in some OSes. Keep it empty otherwise. 406 }{ 407 {testutils.ProgramHelloworld, testutils.HelloworldAddrMain, ""}, 408 {testutils.ProgramHelloworldNoDwarf, testutils.HelloworldAddrMain, ""}, // includes the last 0xcc insts 409 {testutils.ProgramHelloworld, testutils.HelloworldAddrGoBuildID, "darwin"}, // includes bad insts 410 } { 411 if testdata.targetOS != "" && testdata.targetOS != runtime.GOOS { 412 continue 413 } 414 415 proc, err := LaunchProcess(testdata.program, nil, helloworldAttr) 416 if err != nil { 417 t.Fatalf("failed to launch process: %v", err) 418 } 419 defer proc.Detach() 420 421 f, err := proc.FindFunction(testdata.funcAddr) 422 if err != nil { 423 t.Fatalf("failed to find function: %v", err) 424 } 425 insts, err := proc.ReadInstructions(f) 426 if err != nil { 427 t.Fatalf("failed to read instructions: %v", err) 428 } 429 430 if len(insts) == 0 { 431 t.Errorf("empty insts") 432 } 433 } 434 } 435 436 func TestReadInstructions_SetBreakpointBefore(t *testing.T) { 437 proc, err := LaunchProcess(testutils.ProgramHelloworld, nil, helloworldAttr) 438 if err != nil { 439 t.Fatalf("failed to launch process: %v", err) 440 } 441 defer proc.Detach() 442 443 f, err := proc.FindFunction(testutils.HelloworldAddrMain) 444 if err != nil { 445 t.Fatalf("failed to find function: %v", err) 446 } 447 448 proc.SetBreakpoint(f.StartAddr) 449 450 insts, err := proc.ReadInstructions(f) 451 if err != nil { 452 t.Fatalf("failed to read instructions: %v", err) 453 } 454 455 if len(insts) == 0 { 456 t.Errorf("empty insts") 457 } 458 if insts[0].Op == x86asm.INT { 459 t.Errorf("breakpoint is not reset") 460 } 461 } 462 463 func TestCurrentGoRoutineInfo(t *testing.T) { 464 for i, testProgram := range []string{testutils.ProgramHelloworld, testutils.ProgramHelloworldNoDwarf} { 465 proc, err := LaunchProcess(testProgram, nil, helloworldAttr) 466 if err != nil { 467 t.Fatalf("[%d] failed to launch process: %v", i, err) 468 } 469 defer proc.Detach() 470 471 if err := proc.SetBreakpoint(testutils.HelloworldAddrMain); err != nil { 472 t.Fatalf("[%d] failed to set breakpoint: %v", i, err) 473 } 474 475 event, err := proc.ContinueAndWait() 476 if err != nil { 477 t.Fatalf("[%d] failed to continue and wait: %v", i, err) 478 } 479 480 threadIDs := event.Data.([]int) 481 goRoutineInfo, err := proc.CurrentGoRoutineInfo(threadIDs[0]) 482 if err != nil { 483 t.Fatalf("[%d] error: %v", i, err) 484 } 485 if goRoutineInfo.ID != 1 { 486 t.Errorf("[%d] wrong id: %d", i, goRoutineInfo.ID) 487 } 488 if goRoutineInfo.UsedStackSize == 0 { 489 t.Errorf("[%d] wrong stack size: %d", i, goRoutineInfo.UsedStackSize) 490 } 491 if goRoutineInfo.CurrentPC != testutils.HelloworldAddrMain+1 { 492 t.Errorf("[%d] wrong pc: %d", i, goRoutineInfo.CurrentPC) 493 } 494 if goRoutineInfo.CurrentStackAddr == 0 { 495 t.Errorf("[%d] current stack address is 0", i) 496 } 497 if goRoutineInfo.NextDeferFuncAddr == 0 { 498 t.Errorf("[%d] NextDeferFuncAddr is 0", i) 499 } 500 if goRoutineInfo.Panicking { 501 t.Errorf("[%d] panicking", i) 502 } 503 // main go routine always has 'defer' setting. See runtime.main() for the detail. 504 if goRoutineInfo.PanicHandler == nil || goRoutineInfo.PanicHandler.PCAtDefer == 0 || goRoutineInfo.PanicHandler.UsedStackSizeAtDefer == 0 { 505 t.Errorf("[%d] deferedBy is nil or its value is 0", i) 506 } 507 } 508 } 509 510 func TestCurrentGoRoutineInfo_Panicking(t *testing.T) { 511 for _, testProgram := range []string{testutils.ProgramPanic, testutils.ProgramPanicNoDwarf} { 512 proc, err := LaunchProcess(testProgram, nil, helloworldAttr) 513 if err != nil { 514 t.Fatalf("failed to launch process: %v", err) 515 } 516 defer proc.Detach() 517 518 if err := proc.SetBreakpoint(testutils.PanicAddrInsideThrough); err != nil { 519 t.Fatalf("failed to set breakpoint: %v", err) 520 } 521 522 event, err := proc.ContinueAndWait() 523 if err != nil { 524 t.Fatalf("failed to continue and wait: %v", err) 525 } 526 527 tids := event.Data.([]int) 528 goRoutineInfo, err := proc.CurrentGoRoutineInfo(tids[0]) 529 if err != nil { 530 t.Fatalf("error: %v", err) 531 } 532 if !goRoutineInfo.Panicking { 533 t.Errorf("not panicking") 534 } 535 536 if goRoutineInfo.PanicHandler.PCAtDefer == 0 { 537 t.Errorf("invalid panic handler") 538 } 539 } 540 } 541 542 func TestArgument_ParseValue(t *testing.T) { 543 for i, testdata := range []struct { 544 arg Argument 545 expected string 546 }{ 547 {Argument{Name: "a", parseValue: func(int) value { return int8Value{val: 1} }}, "a = 1"}, 548 {Argument{Name: "a", parseValue: func(int) value { return nil }}, "a = -"}, 549 {Argument{Name: "", parseValue: func(int) value { return int8Value{val: 1} }}, "1"}, 550 } { 551 actual := testdata.arg.ParseValue(0) 552 if actual != testdata.expected { 553 t.Errorf("[%d] wrong parsed result. expect: %s, actual %s", i, testdata.expected, actual) 554 } 555 } 556 557 }