]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gprofng/src/gp-display-src.cc
Update year range in gprofng copyright notices
[thirdparty/binutils-gdb.git] / gprofng / src / gp-display-src.cc
CommitLineData
76bdc726 1/* Copyright (C) 2021-2023 Free Software Foundation, Inc.
bb368aad
VM
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
36class er_src : public DbeApplication
37{
38public:
39 er_src (int argc, char *argv[]);
40 void start (int argc, char *argv[]);
41
42private:
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
88static int
89real_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
97int
98main (int argc, char *argv[])
99{
100 return catch_out_of_memory (real_main, argc, argv);
101}
102
103er_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
118static int
119FuncNameCmp (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
126static int
127FuncAddrCmp (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
135void
136er_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
194void
195er_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
207FILE *
208er_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
244void
245er_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
334void
335er_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
485int
486er_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
588void
589er_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
608void
609er_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
620void
621er_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
650static bool
651isFatal (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
668void
669er_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}