github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/debug.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "sort" 9 "strings" 10 11 "github.com/Konstantin8105/c4go/ast" 12 "github.com/Konstantin8105/c4go/preprocessor" 13 ) 14 15 type Positioner interface { 16 Position() ast.Position 17 Inject(lines [][]byte, filePP preprocessor.FilePP) error 18 } 19 20 type cases struct { 21 name string 22 pos ast.Position 23 } 24 25 func (f cases) Position() ast.Position { 26 return f.pos 27 } 28 29 func (f cases) Inject(lines [][]byte, filePP preprocessor.FilePP) error { 30 31 b, err := getByte(lines, f.pos) 32 if err != nil { 33 return err 34 } 35 36 if b != 'c' { 37 return fmt.Errorf("unacceptable char 'c' : %c", lines[f.pos.Line-1][f.pos.Column-1]) 38 } 39 40 col := f.pos.Column - 1 41 found := false 42 for ; col < len(lines[f.pos.Line-1]); col++ { 43 if lines[f.pos.Line-1][col] == ':' { 44 found = true 45 break 46 } 47 } 48 49 if !found { 50 return fmt.Errorf("cannot find char ':' : %s", lines[f.pos.Line-1]) 51 } 52 53 // compare line of code 54 { 55 buf, err := filePP.GetSnippet(f.pos.File, f.pos.Line, f.pos.Line, 0, f.pos.Column) 56 if err != nil { 57 return err 58 } 59 if !bytes.Equal(lines[f.pos.Line-1][:f.pos.Column], buf) { 60 return fmt.Errorf("lines in source and pp source is not equal") 61 } 62 } 63 64 f.pos.Column = col + 1 65 66 lines[f.pos.Line-1] = append(lines[f.pos.Line-1][:f.pos.Column], 67 append([]byte(fmt.Sprintf(";%s(%d,\"%s\");", debugFunctionName, f.pos.Line, f.name)), 68 lines[f.pos.Line-1][f.pos.Column:]...)...) 69 70 return nil 71 } 72 73 type compount struct { 74 name string 75 pos ast.Position 76 } 77 78 func (f compount) Position() ast.Position { 79 return f.pos 80 } 81 82 func (f compount) Inject(lines [][]byte, filePP preprocessor.FilePP) error { 83 84 b, err := getByte(lines, f.pos) 85 if err != nil { 86 return err 87 } 88 89 if b != '{' { 90 return fmt.Errorf("unacceptable char '{' : %c", lines[f.pos.Line-1][f.pos.Column-1]) 91 } 92 93 // compare line of code 94 { 95 buf, err := filePP.GetSnippet(f.pos.File, f.pos.Line, f.pos.Line, 0, f.pos.Column) 96 if err != nil { 97 return err 98 } 99 if !bytes.Equal(lines[f.pos.Line-1][:f.pos.Column], buf) { 100 return fmt.Errorf("lines in source and pp source is not equal") 101 } 102 } 103 104 lines[f.pos.Line-1] = append(lines[f.pos.Line-1][:f.pos.Column], 105 append([]byte(fmt.Sprintf(";%s(%d,\"%s\");", debugFunctionName, f.pos.Line, f.name)), 106 lines[f.pos.Line-1][f.pos.Column:]...)...) 107 108 return nil 109 } 110 111 func getByte(lines [][]byte, pos ast.Position) (b byte, err error) { 112 if pos.Line-1 <= 0 { 113 err = fmt.Errorf("outside line") 114 return 115 } 116 if pos.Line-1 >= len(lines) { 117 err = fmt.Errorf("try to add debug on outside of allowable line: %v", pos) 118 return 119 } 120 if pos.Column-1 >= len(lines[pos.Line-1]) { 121 err = fmt.Errorf("try to add debug on outside of allowable column: %v", pos) 122 return 123 } 124 125 b = lines[pos.Line-1][pos.Column-1] 126 return 127 } 128 129 type argument struct { 130 pos ast.Position 131 description string 132 varName string 133 cType string 134 } 135 136 func (v argument) Position() ast.Position { 137 return v.pos 138 } 139 140 func (v argument) Inject(lines [][]byte, filePP preprocessor.FilePP) error { 141 // v.cType = strings.Replace(v.cType, "const ", "", -1) 142 143 if v.pos.Line-1 <= 0 { 144 return fmt.Errorf("outside lines") 145 } 146 147 if v.pos.Column-1 >= len(lines[v.pos.Line-1]) { 148 return fmt.Errorf("column is outside line") 149 } 150 151 // compare line of code 152 { 153 buf, err := filePP.GetSnippet(v.pos.File, v.pos.Line, v.pos.Line, 0, v.pos.Column) 154 if err != nil { 155 return err 156 } 157 if !bytes.Equal(lines[v.pos.Line-1][:v.pos.Column], buf) { 158 return fmt.Errorf("lines in source and pp source is not equal") 159 } 160 } 161 162 var index int = -1 163 for i := range FuncArgs { 164 if FuncArgs[i].cType == v.cType { 165 index = i 166 } 167 } 168 if index >= 0 { 169 // find argument type 170 function := fmt.Sprintf(";%s%s(%d,\"%s\",\"%s\",%s);", 171 debugArgument, FuncArgs[index].postfix, 172 v.pos.Line, v.description, v.varName, v.varName) 173 lines[v.pos.Line-1] = append(lines[v.pos.Line-1][:v.pos.Column], 174 append([]byte(function), lines[v.pos.Line-1][v.pos.Column:]...)...) 175 } else if v.cType == "char *" || v.cType == "const char *" { 176 function := fmt.Sprintf(";%s(%d,\"%s\",\"%s\",%s);", 177 debugArgumentString, 178 v.pos.Line, v.description, v.varName, v.varName) 179 lines[v.pos.Line-1] = append(lines[v.pos.Line-1][:v.pos.Column], 180 append([]byte(function), lines[v.pos.Line-1][v.pos.Column:]...)...) 181 } 182 183 return nil 184 } 185 186 func generateDebugCCode(args ProgramArgs, lines []string, filePP preprocessor.FilePP) ( 187 err error) { 188 if args.verbose { 189 fmt.Fprintln(os.Stdout, "Convert ast lines to ast tree") 190 } 191 192 // convert lines to tree ast 193 tree, errs := FromLinesToTree(args.verbose, lines, filePP) 194 for i := range errs { 195 fmt.Fprintf(os.Stderr, "AST error #%d:\n%v\n", 196 i, errs[i].Error()) 197 } 198 if tree == nil { 199 return fmt.Errorf("cannot create tree: tree is nil. Please try another version of clang") 200 } 201 202 // Example of AST: 203 // 204 // TranslationUnitDecl 205 // |-TypedefDecl 206 // | `-... 207 // |-FunctionDecl used a 'void (int *)' 208 // |-FunctionDecl 209 // | |-ParmVarDecl 210 // | `-CompoundStmt 211 // | `-... 212 213 if len(tree) == 0 { 214 return fmt.Errorf("tree is empty") 215 } 216 217 if args.verbose { 218 fmt.Fprintln(os.Stdout, "Walking by tree...") 219 } 220 221 // map[filename] []funcPos 222 funcPoses := map[string][]Positioner{} 223 224 for i := range tree { 225 tr, ok := tree[i].(*ast.TranslationUnitDecl) 226 if !ok { 227 return fmt.Errorf("first node %d is not TranslationUnitDecl: %d", i, tree[i]) 228 } 229 for j := range tr.Children() { 230 // is it FunctionDecl 231 fd, ok := tr.Children()[j].(*ast.FunctionDecl) 232 if !ok { 233 continue 234 } 235 if len(fd.Children()) == 0 { 236 continue 237 } 238 // have a body 239 mst, ok := fd.Children()[len(fd.Children())-1].(*ast.CompoundStmt) 240 if !ok { 241 continue 242 } 243 // is user source 244 if !filePP.IsUserSource(mst.Position().File) { 245 continue 246 } 247 248 // initialize slice 249 if _, ok := funcPoses[mst.Position().File]; !ok { 250 funcPoses[mst.Position().File] = make([]Positioner, 0, 10) 251 } 252 253 if args.verbose { 254 fmt.Fprintf(os.Stdout, "find function : %s\n", fd.Name) 255 } 256 257 // Example for input function input data: 258 // 259 // FunctionDecl used readline 'char *(char *, FILE *, char *)' 260 // |-ParmVarDecl used string 'char *' 261 // |-ParmVarDecl used infile 'FILE *' 262 // |-ParmVarDecl used infilename 'char *' 263 // `-CompoundStmt 264 // |-... 265 // 266 // FunctionDecl used tolower 'long (int, int)' 267 // |-ParmVarDecl used a 'int' 268 // |-ParmVarDecl used b 'int' 269 // `-CompoundStmt 270 // `-... 271 272 // function name 273 { 274 f := compount{ 275 name: "func " + fd.Name, 276 pos: mst.Position(), 277 } 278 sl, _ := funcPoses[mst.Position().File] 279 sl = append(sl, f) 280 funcPoses[mst.Position().File] = sl 281 } 282 283 // function variable 284 for k := range fd.Children() { 285 parm, ok := fd.Children()[k].(*ast.ParmVarDecl) 286 if !ok { 287 continue 288 } 289 p := argument{ 290 varName: parm.Name, 291 pos: mst.Position(), 292 description: fmt.Sprintf("%d", k), 293 cType: parm.Type, 294 } 295 sl, _ := funcPoses[mst.Position().File] 296 sl = append(sl, p) 297 funcPoses[mst.Position().File] = sl 298 } 299 300 var injector inj 301 injector.walk(fd.Children()[len(fd.Children())-1]) 302 list := injector.getPositioner() 303 for p := range list { 304 file := list[p].Position().File 305 sl, _ := funcPoses[file] 306 funcPoses[file] = append(sl, list[p]) 307 } 308 } 309 } 310 311 if args.verbose { 312 fmt.Fprintf(os.Stdout, "found %d files with functions\n", len(funcPoses)) 313 } 314 315 for file, positions := range funcPoses { 316 // sort from end to begin 317 sort.SliceStable(positions, func(i, j int) bool { 318 if positions[i].Position().Line == positions[j].Position().Line { 319 return positions[i].Position().Column < positions[j].Position().Column 320 } 321 return positions[i].Position().Line < positions[j].Position().Line 322 }) 323 324 // read present file 325 dat, err := ioutil.ReadFile(file) 326 if err != nil { 327 return err 328 } 329 330 if args.verbose { 331 fmt.Fprintln(os.Stdout, "inject debug information in file: ", file) 332 } 333 334 // inject function 335 lines := bytes.Split(dat, []byte("\n")) 336 for k := len(positions) - 1; k >= 0; k-- { 337 err2 := positions[k].Inject(lines, filePP) 338 if err2 != nil { 339 // error is ignored 340 _ = err2 341 } else { 342 // non error is ignored 343 } 344 } 345 346 // add main debug function 347 lines = append([][]byte{[]byte(debugCode())}, lines...) 348 349 filename := file 350 // create a new filename 351 if index := strings.LastIndex(file, "/"); index >= 0 { 352 filename = file[:index+1] + args.debugPrefix + file[index+1:] 353 } else { 354 filename = args.debugPrefix + file 355 } 356 357 if args.verbose { 358 fmt.Fprintln(os.Stdout, "Write file with debug information in file: ", filename) 359 } 360 361 // save file with prefix+filename 362 err = ioutil.WriteFile(filename, bytes.Join(lines, []byte{'\n'}), 0644) 363 if err != nil { 364 return err 365 } 366 } 367 368 return nil 369 } 370 371 const ( 372 debugFunctionName string = "c4go_debug_compount" 373 debugArgument string = "c4go_debug_function_arg_" 374 debugArgumentString string = "c4go_debug_function_arg_string" 375 ) 376 377 func debugCode() string { 378 body := ` 379 #include <stdio.h> 380 #include <stdlib.h> 381 382 FILE * c4go_get_debug_file() 383 { 384 FILE * file; 385 file = fopen("./debug.txt","a"); 386 if(file==NULL){ 387 exit(53); 388 }; 389 return file; 390 } 391 392 void c4go_debug_compount(int line, char * functionName) 393 { 394 FILE * file = c4go_get_debug_file(); 395 fprintf(file,"Line: %d. name: %s\n",line, functionName); 396 fclose(file); 397 } 398 399 #define c4go_arg(type, postfix, format) \ 400 void c4go_debug_function_arg_##postfix(int line, char * arg_pos, char * name, type arg_value) \ 401 { \ 402 FILE * file = c4go_get_debug_file(); \ 403 fprintf(file,"Line: %d\n",line); \ 404 fprintf(file,"\tdescription: %s\n", arg_pos); \ 405 fprintf(file,"\tname: %s\n", name); \ 406 fprintf(file,"\tval : \""); \ 407 fprintf(file,format, arg_value); \ 408 fprintf(file,"\"\n"); \ 409 fclose(file); \ 410 } 411 412 void c4go_debug_function_arg_string(int line, const char * arg_pos, const char * name,const char * arg_value) 413 { 414 FILE * file = c4go_get_debug_file(); 415 fprintf(file,"Line: %d\n",line); 416 fprintf(file,"\tdescription: %s\n", arg_pos); 417 fprintf(file,"\tname: %s\n", name); 418 fprintf(file,"\tval : \""); 419 if (arg_value == NULL) { 420 fprintf(file, "<null>"); 421 } else { 422 fprintf(file, "%s" , arg_value); 423 } 424 fprintf(file,"\"\n"); 425 fclose(file); 426 } 427 428 ` 429 430 for i := range FuncArgs { 431 body += fmt.Sprintf("\nc4go_arg(%s,%s,\"%s\");\n", 432 FuncArgs[i].cType, FuncArgs[i].postfix, FuncArgs[i].format) 433 } 434 435 return body 436 } 437 438 var FuncArgs = []struct { 439 cType string 440 postfix string 441 format string 442 }{ 443 {"int", "int", "%d"}, 444 {"char", "char", "%d"}, 445 {"unsigned int", "uint", "%d"}, 446 {"long", "long", "%ld"}, 447 {"float", "float", "%f"}, 448 {"double", "double", "%f"}, 449 // {"int *", "pnt_int", "%d"}, 450 // {"char *", "string", "%s"}, 451 // {"char **", "double_string", "%s"}, 452 // {"unsigned char *", "ustring", "%s"}, 453 } 454 455 type inj struct { 456 poss []Positioner 457 varDecls []argument 458 insideBinaryOperator bool 459 } 460 461 func (in *inj) addVarDecl(arg argument) { 462 // add only uniq VarDecls 463 for i := range in.varDecls { 464 if in.varDecls[i].varName == arg.varName && 465 in.varDecls[i].cType == arg.cType { 466 return 467 } 468 } 469 in.varDecls = append(in.varDecls, arg) 470 } 471 472 func (in *inj) getPositioner() []Positioner { 473 return in.poss 474 } 475 476 func (in *inj) newAllowablePosition(pos ast.Position) { 477 // add Positioner after symbol `pos` 478 for k := len(in.varDecls) - 1; k >= 0; k-- { 479 // avoid names intersection 480 var ignore bool 481 for g := len(in.varDecls) - 1; g > k; g-- { 482 if in.varDecls[k].varName == in.varDecls[g].varName || 483 "*"+in.varDecls[k].varName == in.varDecls[g].varName || 484 in.varDecls[k].varName == "*"+in.varDecls[g].varName { 485 ignore = true 486 break 487 } 488 } 489 if ignore { 490 continue 491 } 492 // add all ast.VarDecl 493 vd := in.varDecls[k] 494 vd.pos = pos 495 in.poss = append(in.poss, vd) 496 } 497 } 498 499 func (in *inj) walk(node ast.Node) { 500 // ignore nil node 501 if node == nil { 502 return 503 } 504 505 switch v := node.(type) { 506 case *ast.CompoundStmt: 507 in.poss = append(in.poss, compount{name: "CompoundStmt", pos: node.Position()}) 508 in.newAllowablePosition(node.Position()) 509 size := len(in.varDecls) 510 for i := 0; i < len(node.Children()); i++ { 511 in.walk(node.Children()[i]) // walking inside 512 } 513 if size < len(in.varDecls) { // remove last VarDecls, if some added 514 in.varDecls = in.varDecls[:size] 515 } 516 517 case *ast.ArraySubscriptExpr: 518 // ignore 519 return 520 521 case *ast.IfStmt: 522 // IfStmt 523 // |-<<<NULL>>> 524 // |-<<<NULL>>> 525 // |-BinaryOperator 'int' '!=' 526 // | `-... 527 // |-CompoundStmt 528 // | `-... 529 // `-<<<NULL>>> 530 for i := 3; i < len(v.Children()); i++ { 531 in.walk(v.Children()[i]) 532 } 533 return 534 535 case *ast.ForStmt: 536 // ForStmt 537 // |-... // check this 538 // |-<<<NULL>>> 539 // |-... 540 // |-... 541 // `-CompoundStmt // check this 542 size := len(in.varDecls) 543 for i := 0; i < len(v.Children()); i++ { 544 in.walk(v.Children()[i]) 545 } 546 if size < len(in.varDecls) { // remove last VarDecls, if some added 547 in.varDecls = in.varDecls[:size] 548 } 549 return 550 551 case *ast.WhileStmt: 552 // WhileStmt 553 // |-<<<NULL>>> 554 // |-BinaryOperator 'int' '<=' 555 // | `-... 556 // `-CompoundStmt 557 // |-... 558 for i := 2; i < len(v.Children()); i++ { 559 in.walk(v.Children()[i]) 560 } 561 return 562 563 case *ast.DefaultStmt: 564 // that node bug in column identification 565 return 566 567 case *ast.ImplicitCastExpr: 568 in.walk(v.Children()[0]) 569 return 570 571 case *ast.CallExpr: 572 // CallExpr 'double' 573 // |-ImplicitCastExpr 'double (*)(int, float, double)' <LValueToRValue> 574 // | `-DeclRefExpr 'double (*)(int, float, double)' lvalue ParmVar 0x42be310 'F' 'double (*)(int, float, double)' 575 // |-... 576 // |-... 577 // `-... 578 for i := 1; i < len(v.Children()); i++ { 579 in.walk(v.Children()[i]) 580 } 581 // not in BinaryOperator 582 if in.insideBinaryOperator { 583 return 584 } 585 // new place at the end of CallExpr: 586 // memmove(...,...,...); 587 // |--- end position is here 588 if v.Pos.LineEnd != 0 { 589 v.Pos.Line = v.Pos.LineEnd 590 } 591 v.Pos.Column = v.Pos.ColumnEnd + 1 592 in.newAllowablePosition(v.Pos) 593 in.poss = append(in.poss, compount{name: "After CallExpr", pos: v.Pos}) 594 return 595 596 case *ast.CaseStmt: 597 // TODO: is same source line 598 // TODO: create a newAllowablePosition 599 // TODO: addCase("case", node) 600 // 601 // walking by tree 602 // addCase := func(name string, node ast.Node) { 603 // sl, _ := funcPoses[node.Position().File] 604 // sl = append(sl, cases{name: name, pos: node.Position()}) 605 // funcPoses[node.Position().File] = sl 606 // } 607 // } 608 609 case *ast.VarDecl: 610 // VarDecl with initialization 611 if len(v.Children()) > 0 { 612 in.addVarDecl(argument{ 613 // Not define Position 614 description: "VarDecl", 615 varName: v.Name, 616 cType: v.Type, 617 }) 618 } 619 620 case *ast.DeclRefExpr: 621 in.addVarDecl(argument{ 622 // Not define Position 623 description: "BinEQ_Decl", 624 varName: v.Name, 625 cType: v.Type, 626 }) 627 628 case *ast.MemberExpr: 629 if decl, ok := v.Children()[0].(*ast.DeclRefExpr); ok { 630 in.addVarDecl(argument{ 631 // Not define Position 632 description: "BinEQ_MemDecl", 633 varName: fmt.Sprintf("%s.%s", decl.Name, v.Name), 634 cType: v.Type, 635 }) 636 return 637 } 638 if impl, ok := v.Children()[0].(*ast.ImplicitCastExpr); ok { 639 if decl, ok := impl.Children()[0].(*ast.DeclRefExpr); ok { 640 if v.IsPointer { 641 in.addVarDecl(argument{ 642 // Not define Position 643 description: "BinEQ_MemImpDeclP", 644 varName: fmt.Sprintf("%s->%s", decl.Name, v.Name), 645 cType: v.Type, 646 }) 647 return 648 } 649 in.addVarDecl(argument{ 650 // Not define Position 651 description: "BinEQ_MemImpDecl", 652 varName: fmt.Sprintf("%s.%s", decl.Name, v.Name), 653 cType: v.Type, 654 }) 655 return 656 } 657 } 658 659 case *ast.UnaryOperator: 660 if v.Operator == "*" { 661 if impl, ok := v.Children()[0].(*ast.ImplicitCastExpr); ok { 662 if decl, ok := impl.Children()[0].(*ast.DeclRefExpr); ok { 663 in.addVarDecl(argument{ 664 // Not define Position 665 description: "UID", 666 varName: fmt.Sprintf("*%s", decl.Name), 667 cType: v.Type, 668 }) 669 } 670 } 671 } 672 673 case *ast.BinaryOperator: 674 in.insideBinaryOperator = true 675 defer func() { 676 in.insideBinaryOperator = false 677 }() 678 for pos := range v.Children() { 679 in.walk(v.Children()[pos]) 680 } 681 682 case *ast.DeclStmt: 683 for pos := range v.Children() { 684 in.walk(v.Children()[pos]) 685 } 686 } 687 }