]> Git Repo - binutils.git/blob - gprofng/src/gp-display-src.cc
Automatic date update in version.in
[binutils.git] / gprofng / src / gp-display-src.cc
1 /* Copyright (C) 2021 Free Software Foundation, Inc.
2    Contributed by Oracle.
3
4    This file is part of GNU Binutils.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3, or (at your option)
9    any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, 51 Franklin Street - Fifth Floor, Boston,
19    MA 02110-1301, USA.  */
20
21 #include "config.h"
22 #include <unistd.h>
23 #include <fcntl.h>
24
25 #include "util.h"
26 #include "DbeApplication.h"
27 #include "DbeSession.h"
28 #include "Function.h"
29 #include "LoadObject.h"
30 #include "Module.h"
31 #include "DbeView.h"
32 #include "Print.h"
33 #include "DbeFile.h"
34 #include "Command.h"
35
36 class er_src : public DbeApplication
37 {
38 public:
39   er_src (int argc, char *argv[]);
40   void start (int argc, char *argv[]);
41
42 private:
43
44   // override methods in base class
45   void usage ();
46   int check_args (int argc, char *argv[]);
47   void run_args (int argc, char *argv[]);
48
49   enum Obj_Types
50   {
51     OT_EXE_ELF = 0, OT_JAVA_CLASS, OT_JAR_FILE, OT_UNKNOWN
52   };
53
54   void open (char *exe);
55   void dump_annotated (char *name, char* sel, char *src, DbeView *dbev,
56                        bool is_dis, bool first);
57   void checkJavaClass (char *exe);
58   void print_header (bool first, const char* text);
59   void proc_cmd (CmdType cmd_type, bool first, char *arg1,
60                  const char *arg2, const char *arg3 = NULL);
61   FILE *set_outfile (char *cmd, FILE *&set_file);
62
63   bool is_java_class ()     { return obj_type == OT_JAVA_CLASS; }
64
65   int dbevindex;
66   DbeView *dbev;
67   LoadObject *lo;
68   Obj_Types obj_type;
69   const char *out_fname;
70   FILE *out_file;
71   bool isDisasm;
72   bool isFuncs;
73   bool isDFuncs;
74   bool isSrc;
75   bool v_opt;
76   int multiple;
77   char *str_compcom;
78   bool hex_visible;
79   int src_visible;
80   int vis_src;
81   int vis_dis;
82   int threshold_src;
83   int threshold_dis;
84   int threshold;
85   int vis_bits;
86 };
87
88 static int
89 real_main (int argc, char *argv[])
90 {
91   er_src *src = new er_src (argc, argv);
92   src->start (argc, argv);
93   delete src;
94   return 0;
95 }
96
97 int
98 main (int argc, char *argv[])
99 {
100   return catch_out_of_memory (real_main, argc, argv);
101 }
102
103 er_src::er_src (int argc, char *argv[])
104 : DbeApplication (argc, argv)
105 {
106   obj_type = OT_UNKNOWN;
107   out_fname = "<stdout>";
108   out_file = stdout;
109   isDisasm = false;
110   isFuncs = false;
111   isDFuncs = false;
112   isSrc = false;
113   v_opt = false;
114   multiple = 0;
115   lo = NULL;
116 }
117
118 static int
119 FuncNameCmp (const void *a, const void *b)
120 {
121   Function *item1 = *((Function **) a);
122   Function *item2 = *((Function **) b);
123   return strcmp (item1->get_mangled_name (), item2->get_mangled_name ());
124 }
125
126 static int
127 FuncAddrCmp (const void *a, const void *b)
128 {
129   Function *item1 = *((Function **) a);
130   Function *item2 = *((Function **) b);
131   return (item1->img_offset == item2->img_offset) ?
132           FuncNameCmp (a, b) : item1->img_offset > item2->img_offset ? 1 : -1;
133 }
134
135 void
136 er_src::usage ()
137 {
138
139 /*
140   Ruud - Isolate this line because it has an argument.  Otherwise it would be at the
141   end of a long usage list.
142 */
143   printf ( GTXT (
144     "Usage: gprofng display src [OPTION(S)] TARGET-OBJECT\n"));
145
146   printf ( GTXT (
147     "\n"
148     "Display the source code listing, or source code interleaved with disassembly code,\n"
149     "as extracted from the target object (an executable, shared object, object file, or\n"
150     "a Java .class file).\n"
151     "\n"
152     "Options:\n"
153     "\n"
154     " --version           print the version number and exit.\n"
155     " --help              print usage information and exit.\n"
156     " --verbose {on|off}  enable (on) or disable (off) verbose mode; the default is \"off\".\n"
157     "\n"
158     " -func                   list all the functions from the given object.\n"
159     "\n"
160     " -source item tag    show the source code for item; the tag is used to\n"
161     "                     differentiate in case of multiple occurences with\n"
162     "                     the same name; the combination of \"all -1\" selects\n"
163     "                     all the functions in the object; the default is\n"
164     "                     \"-source all -1\".\n"
165     "\n"
166     " -disasm item tag        show the source code, interleaved with the disassembled\n"
167     "                         instructions; the same definitions for item and tag apply.\n"
168     "\n"
169     " -outfile <filename>     write results to file <filename>; a dash (-) writes to\n"
170     "                         stdout; this is also the default; note that this only\n"
171     "                         affects options included to the right of this option.\n"
172     "\n"
173    "Documentation:\n"
174     "\n"
175     "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n"
176     "gprofng programs are properly installed at your site, the command \"info gprofng\"\n"
177     "should give you access to this document.\n"
178     "\n"
179     "See also:\n"
180     "\n"
181     "gprofng(1), gp-archive(1), gp-collect-app(1), gp-display-html(1), gp-display-text(1)\n"));
182 /*
183   printf (GTXT ("Usage: %s [OPTION] a.out/.so/.o/.class\n\n"), whoami);
184   printf (GTXT ("    -func                     List all the functions from the given object\n"
185                 "    -source, -src item tag    Show the annotated source for the listed item\n"
186                 "    -disasm item tag          Include the disassembly in the listing\n"
187                 "    -V                        Print the current release version of er_src\n"
188                 "    -cc, -scc, -dcc com_spec  Define the compiler commentary classes to show\n"
189                 "    -outfile filename         Open filename for output\n"));
190 */
191   exit (0);
192 }
193
194 void
195 er_src::start (int argc, char *argv[])
196 {
197   dbevindex = dbeSession->createView (0, -1);
198   dbev = dbeSession->getView (dbevindex);
199
200   // get options
201   check_args (argc, argv);
202   run_args (argc, argv);
203   if (out_file != stdout)
204     fclose (out_file);
205 }
206
207 FILE *
208 er_src::set_outfile (char *cmd, FILE *&set_file)
209 {
210   FILE *new_file;
211   if (!strcasecmp (cmd, "-"))
212     {
213       new_file = stdout;
214       out_fname = "<stdout>";
215     }
216   else
217     {
218       char *cmdpath;
219       char *fname = strstr (cmd, "~/");
220       // Handle ~ in file names
221       char *home = getenv ("HOME");
222       if (fname != NULL && home != NULL)
223         cmdpath = dbe_sprintf ("%s/%s", home, fname + 2);
224       else if ((fname = strstr (cmd, "~")) != NULL && home != NULL)
225         cmdpath = dbe_sprintf ("/home/%s", fname + 1);
226       else
227         cmdpath = strdup (cmd);
228       new_file = fopen (cmdpath, "w");
229       if (new_file == NULL)
230         {
231           fprintf (stderr, GTXT ("Unable to open file: %s"), cmdpath);
232           free (cmdpath);
233           return NULL;
234         }
235       out_fname = cmdpath;
236     }
237   if (set_file && (set_file != stdout))
238     fclose (set_file);
239
240   set_file = new_file;
241   return set_file;
242 }
243
244 void
245 er_src::proc_cmd (CmdType cmd_type, bool first, char *arg1,
246                   const char *arg2, const char *arg3)
247 {
248   Cmd_status status;
249   Module *module;
250   Function *fitem;
251   int mindex, findex;
252   switch (cmd_type)
253     {
254     case SOURCE:
255       dbev->set_view_mode (VMODE_USER);
256       print_anno_file (arg1, arg2, arg3, false,
257                        stdout, stdin, out_file, dbev, false);
258       break;
259     case DISASM:
260       dbev->set_view_mode (VMODE_MACHINE);
261       print_header (first, GTXT ("Annotated disassembly\n"));
262       print_anno_file (arg1, arg2, arg3, true,
263                        stdout, stdin, out_file, dbev, false);
264       break;
265     case OUTFILE:
266       if (arg1)
267         set_outfile (arg1, out_file);
268       break;
269     case FUNCS:
270       print_header (false, GTXT ("Function list\n"));
271       fprintf (out_file, GTXT ("Functions sorted in lexicographic order\n"));
272       fprintf (out_file, GTXT ("\nLoad Object: %s\n\n"), lo->get_name ());
273       if (lo->wsize == W32)
274         fprintf (out_file, GTXT ("    Address     Size        Name\n\n"));
275       else
276         fprintf (out_file, GTXT ("    Address                     Size        Name\n\n"));
277
278       Vec_loop (Module*, lo->seg_modules, mindex, module)
279       {
280         module->functions->sort (FuncNameCmp);
281         const char *fmt = (lo->wsize == W32) ?
282                 GTXT ("  0x%08llx  %8lld      %s\n") :
283                 GTXT ("  0x%016llx  %16lld      %s\n");
284         Vec_loop (Function*, module->functions, findex, fitem)
285         {
286           fprintf (out_file, fmt,
287                    (ull_t) fitem->img_offset,
288                    (ull_t) fitem->size,
289                    fitem->get_name ());
290         }
291       }
292       break;
293     case DUMPFUNC:
294       lo->functions->sort (FuncAddrCmp);
295       print_header (first, GTXT ("Dump functions\n"));
296       lo->dump_functions (out_file);
297       first = false;
298       break;
299     case SCOMPCOM:
300       status = dbev->proc_compcom (arg1, true, false);
301       if (status != CMD_OK)
302         fprintf (stderr, GTXT ("Error: %s"), Command::get_err_string (status));
303       break;
304     case DCOMPCOM:
305       status = dbev->proc_compcom (arg1, false, false);
306       if (status != CMD_OK)
307         fprintf (stderr, GTXT ("Error: %s"), Command::get_err_string (status));
308       break;
309     case COMPCOM:
310       status = dbev->proc_compcom (arg1, true, false);
311       if (status != CMD_OK)
312         fprintf (stderr, GTXT ("Error: %s: %s"), Command::get_err_string (status), arg1);
313       status = dbev->proc_compcom (arg1, false, false);
314       if (status != CMD_OK)
315         fprintf (stderr, GTXT ("Error: %s: %s"), Command::get_err_string (status), arg1);
316       break;
317     case HELP:
318       usage ();
319       break;
320     case VERSION_cmd:
321       if (out_file != stdout)
322 // Ruud
323         Application::print_version_info ();
324 /*
325         fprintf (out_file, "GNU %s version %s\n", get_basename (prog_name), VERSION);
326 */
327       break;
328     default:
329       fprintf (stderr, GTXT ("Invalid option"));
330       break;
331     }
332 }
333
334 void
335 er_src::run_args (int argc, char *argv[])
336 {
337   CmdType cmd_type;
338   int arg_count, cparam;
339   char *arg;
340   char *arg1;
341   const char *arg2;
342   bool first = true;
343   bool space;
344   Module *module;
345   int mindex;
346
347   for (int i = 1; i < argc; i++)
348     {
349       if (*argv[i] != '-')
350         {
351           if (!multiple)
352             { // er_src -V exe
353               space = false;
354               dbev->set_view_mode (VMODE_USER);
355               print_header (first, GTXT ("Annotated source\n"));
356               Vec_loop (Module*, lo->seg_modules, mindex, module)
357               {
358                 if ((module->flags & MOD_FLAG_UNKNOWN) != 0 ||
359                     module->lang_code == Sp_lang_unknown)
360                   continue;
361                 if (space)
362                   fprintf (out_file, "\n");
363                 print_anno_file (module->file_name, "1", NULL, false,
364                                  stdout, stdin, out_file, dbev, false);
365                 space = true;
366               }
367             }
368           break;
369         }
370       if (strncmp (argv[i], NTXT ("--whoami="), 9) == 0)
371         {
372           whoami = argv[i] + 9;
373           continue;
374         }
375       switch (cmd_type = Command::get_command (argv[i] + 1, arg_count, cparam))
376         {
377         case SOURCE:
378         case DISASM:
379           {
380             i += arg_count;
381             multiple++;
382             if (i >= argc || argv[i] == NULL ||
383                 (*(argv[i]) == '-' && atoi (argv[i]) != -1) || i + 1 == argc)
384               {
385                 i--;
386                 arg = argv[i];
387                 arg2 = "1";
388               }
389             else
390               {
391                 arg = argv[i - 1];
392                 if (*(argv[i]) == '-' && atoi (argv[i]) == -1 &&
393                     streq (arg, NTXT ("all")))
394                   {
395                     space = false;
396                     if (cmd_type == SOURCE)
397                       print_header (first, GTXT ("Annotated source\n"));
398                     else
399                       print_header (first, GTXT ("Annotated disassembly\n"));
400                     Vec_loop (Module*, lo->seg_modules, mindex, module)
401                     {
402                       if ((module->flags & MOD_FLAG_UNKNOWN) != 0 ||
403                           module->lang_code == Sp_lang_unknown)
404                         continue;
405                       if (space)
406                         fprintf (out_file, "\n");
407                       proc_cmd (cmd_type, first, module->file_name, "1");
408                       space = true;
409                     }
410                     first = false;
411                     break;
412                   }
413                 arg2 = argv[i];
414               }
415             char *fcontext = NULL;
416             arg1 = parse_fname (arg, &fcontext);
417             if (arg1 == NULL)
418               {
419                 fprintf (stderr, GTXT ("Error: Invalid function/file setting: %s\n"), arg1);
420                 free (fcontext);
421                 break;
422               }
423             proc_cmd (cmd_type, first, arg1, arg2, fcontext);
424             free (arg1);
425             free (fcontext);
426             first = false;
427             break;
428           }
429         case OUTFILE:
430         case FUNCS:
431         case DUMPFUNC:
432         case COMPCOM:
433         case SCOMPCOM:
434         case DCOMPCOM:
435         case VERSION_cmd:
436         case HELP:
437           proc_cmd (cmd_type, first, (arg_count > 0) ? argv[i + 1] : NULL,
438                     (arg_count > 1) ? argv[i + 2] : NULL);
439           i += arg_count;
440           break;
441         default:
442           if (streq (argv[i] + 1, NTXT ("all")) || streq (argv[i] + 1, NTXT ("dall")))
443             {
444               first = false;
445               multiple++;
446               if (streq (argv[i] + 1, NTXT ("all")))
447                 proc_cmd (FUNCS, first, NULL, NULL);
448               else
449                 proc_cmd (DUMPFUNC, first, NULL, NULL);
450               space = false;
451               print_header (first, GTXT ("Annotated source\n"));
452               Vec_loop (Module*, lo->seg_modules, mindex, module)
453               {
454                 if ((module->flags & MOD_FLAG_UNKNOWN) != 0 ||
455                     module->lang_code == Sp_lang_unknown)
456                   continue;
457                 if (space)
458                   fprintf (out_file, "\n");
459                 proc_cmd (SOURCE, first, module->file_name, "1");
460                 space = true;
461               }
462               print_header (first, GTXT ("Annotated disassembly\n"));
463               Vec_loop (Module*, lo->seg_modules, mindex, module)
464               {
465                 if ((module->flags & MOD_FLAG_UNKNOWN) != 0 ||
466                     module->lang_code == Sp_lang_unknown)
467                   continue;
468                 if (space)
469                   fprintf (out_file, "\n");
470                 proc_cmd (DISASM, first, module->file_name, "1");
471                 space = true;
472               }
473             }
474           else
475             {
476               proc_cmd (cmd_type, first, (arg_count > 0) ? argv[i + 1] : NULL,
477                         (arg_count > 1) ? argv[i + 2] : NULL);
478               i += arg_count;
479               break;
480             }
481         }
482     }
483 }
484
485 int
486 er_src::check_args (int argc, char *argv[])
487 {
488   CmdType cmd_type = UNKNOWN_CMD;
489   int arg_count, cparam;
490   int i;
491   char *exe;
492   bool first = true;
493   if (argc == 1)
494     usage ();
495
496   // If any comments from the .rc files, log them to stderr
497   Emsg * rcmsg = fetch_comments ();
498   while (rcmsg != NULL)
499     {
500       fprintf (stderr, "%s: %s\n", prog_name, rcmsg->get_msg ());
501       rcmsg = rcmsg->next;
502     }
503
504   // Parsing the command line
505   opterr = 0;
506   exe = NULL;
507   for (i = 1; i < argc; i++)
508     {
509       if (*argv[i] != '-')
510         {
511           exe = argv[i];
512           if (i == 1)
513             { // er_src exe ?
514               if (!exe)
515                 usage ();
516               if (argc == 3) // er_src exe file
517                 usage ();
518             }
519           else if (v_opt && !multiple && !exe && !str_compcom) // just er_src -V
520               exit (0);
521           if (argc < i + 1 || argc > i + 3)
522             usage ();
523           i++;
524           if (argc > i)
525             usage ();
526           open (exe);
527           return i;
528         }
529       switch (cmd_type = Command::get_command (argv[i] + 1, arg_count, cparam))
530         {
531         case WHOAMI:
532           whoami = argv[i] + 1 + cparam;
533           break;
534         case HELP:
535           i += arg_count;
536           multiple++;
537           usage ();
538           break;
539         case VERSION_cmd:
540           if (first)
541             {
542 // Ruud
543               Application::print_version_info ();
544 /*
545               printf ("GNU %s version %s\n", get_basename (prog_name), VERSION);
546 */
547               v_opt = true;
548               first = false;
549             }
550           break;
551         case SOURCE:
552         case DISASM:
553           i += arg_count;
554           multiple++;
555           isDisasm = true;
556           if (i >= argc || argv[i] == NULL ||
557               (*(argv[i]) == '-' && atoi (argv[i]) != -1) || (i + 1 == argc))
558             i--;
559           break;
560         case DUMPFUNC:
561           i += arg_count;
562           multiple++;
563           break;
564         case FUNCS:
565           i += arg_count;
566           multiple++;
567           break;
568         case OUTFILE:
569         case COMPCOM:
570         case SCOMPCOM:
571         case DCOMPCOM:
572           i += arg_count;
573           break;
574         default:
575           if (!(streq (argv[i] + 1, NTXT ("all")) ||
576                 streq (argv[i] + 1, NTXT ("dall"))))
577             {
578               fprintf (stderr, "Error: invalid option: `%s'\n", argv[i]);
579               exit (1);
580             }
581         }
582     }
583   if (!exe && !(argc == 2 && cmd_type == VERSION_cmd))
584     usage ();
585   return i;
586 }
587
588 void
589 er_src::checkJavaClass (char* exe)
590 {
591   unsigned char cf_buf[4];
592   unsigned int magic_number;
593   int fd = ::open (exe, O_RDONLY | O_LARGEFILE);
594   if (fd == -1)
595     return;
596   if (sizeof (cf_buf) == read_from_file (fd, cf_buf, sizeof (cf_buf)))
597     {
598       magic_number = cf_buf[0] << 24;
599       magic_number |= cf_buf[1] << 16;
600       magic_number |= cf_buf[2] << 8;
601       magic_number |= cf_buf[3];
602       if (magic_number == 0xcafebabe)
603         obj_type = OT_JAVA_CLASS;
604     }
605   close (fd);
606 }
607
608 void
609 er_src::print_header (bool first, const char* text)
610 {
611   if (!first)
612     fprintf (out_file, "\n");
613   if (multiple > 1)
614     {
615       fprintf (out_file, NTXT ("%s"), text);
616       fprintf (out_file, "---------------------------------------\n");
617     }
618 }
619
620 void
621 er_src::dump_annotated (char *name, char *sel, char *src, DbeView *dbevr,
622                         bool is_dis, bool first)
623 {
624   Module *module;
625   bool space;
626   int mindex;
627   print_header (first, (is_dis) ? ((is_java_class ()) ?
628                                    GTXT ("Annotated bytecode\n") :
629                                    GTXT ("Annotated disassembly\n")) :
630                 GTXT ("Annotated source\n"));
631   if (!name)
632     {
633       space = false;
634       Vec_loop (Module*, lo->seg_modules, mindex, module)
635       {
636         if ((module->flags & MOD_FLAG_UNKNOWN) != 0 ||
637             (!is_dis && module->lang_code == Sp_lang_unknown))
638           continue;
639         if (space)
640           fprintf (out_file, "\n");
641         print_anno_file (module->file_name, sel, src, is_dis,
642                          stdout, stdin, out_file, dbevr, false);
643         space = true;
644       }
645     }
646   else
647     print_anno_file (name, sel, src, is_dis, stdout, stdin, out_file, dbevr, false);
648 }
649
650 static bool
651 isFatal (bool isDisasm, LoadObject::Arch_status status)
652 {
653   if (isDisasm)
654     {
655       switch (status)
656         {
657           // non-fatal errors for disassembly
658         case LoadObject::ARCHIVE_BAD_STABS:
659         case LoadObject::ARCHIVE_NO_STABS:
660           return false;
661         default:
662           return true;
663         }
664     }
665   return true;
666 }
667
668 void
669 er_src::open (char *exe)
670 {
671   LoadObject::Arch_status status;
672   char *errstr;
673   Module *module;
674   Vector<Histable*> *module_lst;
675
676   // Construct the Segment structure
677   char *path = strdup (exe);
678   lo = dbeSession->createLoadObject (path);
679   if (NULL == lo->dbeFile->find_file (lo->dbeFile->get_name ()))
680     {
681       fprintf (stderr, GTXT ("%s: Error: unable to open file %s\n"), prog_name, lo->dbeFile->get_name ());
682       exit (1);
683     }
684   checkJavaClass (exe);
685
686   if (is_java_class ())
687     {
688       lo->type = LoadObject::SEG_TEXT;
689       lo->set_platform (Java, Wnone);
690       lo->id = (uint64_t) - 1; // see AnalyzerSession::ask_which for details
691       module = dbeSession->createClassFile (dbe_strdup (exe));
692       module->loadobject = lo;
693       lo->seg_modules->append (module);
694       module->dbeFile->set_location (exe);
695       if (module->readFile () != module->AE_OK)
696         {
697           Emsg *emsg = module->get_error ();
698           if (emsg)
699             {
700               fprintf (stderr, GTXT ("%s: Error: %s\n"), prog_name, emsg->get_msg ());
701               return;
702             }
703           fprintf (stderr, GTXT ("%s: Error: Could not read class file `%s'\n"), prog_name, exe);
704           return;
705         }
706       status = lo->sync_read_stabs ();
707       if (status != LoadObject::ARCHIVE_SUCCESS)
708         {
709           if (status == LoadObject::ARCHIVE_ERR_OPEN)
710             {
711               fprintf (stderr, GTXT ("%s: Error: Could not read class file `%s'\n"), prog_name, exe);
712               return;
713             }
714           else
715             {
716               if (isDisasm)
717                 if (status == LoadObject::ARCHIVE_NO_STABS)
718                   {
719                     fprintf (stderr, GTXT ("%s: Error: `%s' is interface; disassembly annotation not available\n"), prog_name, exe);
720                     return;
721                   }
722             }
723         }
724     }
725   else
726     {
727       status = lo->sync_read_stabs ();
728       if (status != LoadObject::ARCHIVE_SUCCESS)
729         {
730           errstr = lo->status_str (status);
731           if (errstr)
732             {
733               fprintf (stderr, "%s: %s\n", prog_name, errstr);
734               free (errstr);
735             }
736           if (isFatal (isDisasm, status))
737             return;
738         }
739       obj_type = OT_EXE_ELF;
740
741       // if .o file, then set file as the exe name
742       if (lo->is_relocatable ())
743         {
744           // find the module, if we can
745           module_lst = new Vector<Histable*>;
746           module = dbeSession->map_NametoModule (path, module_lst, 0);
747           if (module == NULL)
748             // Create a module with the right name
749             module = dbeSession->createModule (lo, path);
750         }
751     }
752 }
This page took 0.1 seconds and 4 git commands to generate.