| /* Copyright (C) 2025-2026 Free Software Foundation, Inc. |
| Contributed by Oracle. |
| |
| This file is part of GNU Binutils. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| #include "config.h" |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <iostream> |
| #include <fstream> |
| #include <libgen.h> |
| #include <sys/mman.h> |
| |
| #include "util.h" |
| #include "DbeApplication.h" |
| |
| #include "bfd.h" |
| #include "gmon_io.h" |
| |
| #include "gp-gmon.h" |
| #include "corefile.h" |
| #include "data_pckts.h" |
| #include "call_graph.h" |
| #include "hist.h" |
| |
| #include "symtab.h" |
| #include "cg_arcs.h" |
| |
| #include "gp-experiment.h" |
| #include "collctrl.h" |
| #include "util.h" |
| #include "gp-defs.h" |
| |
| class er_gmon : public DbeApplication |
| { |
| public: |
| er_gmon (int argc, char *argv[]); |
| virtual ~er_gmon (); |
| void start (int argc, char *argv[]); |
| |
| private: |
| // override methods in base class |
| void usage (); |
| void usage_and_exit (int exit_code); |
| int check_mods (int argc, char *argv[]); |
| |
| bool overwrite = false; |
| Coll_Ctrl *cc; |
| }; |
| |
| /* |
| * Default options values: |
| */ |
| #define HZ_WRONG 0 |
| #define INC_DEPTH (128) |
| #define DFN_BUSY -1 |
| #define DFN_NAN 0 |
| #define CLOCK_TYPE OPROF_PCKT |
| |
| /****/ |
| #define ROOT_UID 801425552975190205ULL |
| #define ROOT_UID_INV 92251691606677ULL |
| #define ROOT_IDX 13907816567264074199ULL |
| #define ROOT_IDX_INV 2075111ULL |
| #define UIDTableSize 1048576 |
| |
| #define NATIVE_FRAME_BYTES(nframes) ( ((nframes)+1) * sizeof(long) ) |
| #define OVERHEAD_BYTES ( 2 * sizeof(long) + 2 * sizeof(Stack_info) ) |
| #define DEFAULT_MAX_NFRAMES 256 |
| |
| typedef struct |
| { |
| Sym *sym; |
| } DFN_Stack; |
| |
| typedef struct CM_Array |
| { |
| unsigned int length; /* in bytes, not including length */ |
| void *bytes; |
| } CM_Array; |
| |
| typedef struct ClockPacket |
| { /* clock profiling packet */ |
| CM_Packet comm; |
| pthread_t lwp_id; |
| pthread_t thr_id; |
| uint32_t cpu_id; |
| hrtime_t tstamp __attribute__ ((packed)); |
| uint64_t frinfo __attribute__ ((packed)); |
| int mstate; /* kernel microstate */ |
| int nticks; /* number of ticks in that state */ |
| } ClockPacket; |
| |
| static unsigned long *pc_array = NULL; |
| static char *base_folder; |
| static DFN_Stack *dfn_stack = NULL; |
| static int dfn_maxdepth = 0; |
| static int dfn_depth = 0; |
| static int pc_size = 0; |
| static int pc_maxdepth = 0; |
| static hrtime_t mytime = 0; |
| static uint64_t *UIDTable; |
| |
| // forward declarations |
| static uint64_t get_frame_info (const CM_Array *array); |
| static uint64_t compute_uid (Frame_packet *frp); |
| static void writeBufferToFile (const CM_Packet *pckt, |
| const char *filename); |
| |
| static int |
| real_main (int argc, char *argv[]) |
| { |
| er_gmon *src = new er_gmon (argc, argv); |
| src->start (argc, argv); |
| delete src; |
| return 0; |
| } |
| |
| /* Push a symbol into stack, increase size stack if too low. */ |
| static void |
| pre_visit (Sym *sym) |
| { |
| ++dfn_depth; |
| |
| if (dfn_depth >= dfn_maxdepth) |
| { |
| dfn_maxdepth += INC_DEPTH; |
| dfn_stack = (DFN_Stack *) xrealloc (dfn_stack, |
| dfn_maxdepth * sizeof (*dfn_stack)); |
| } |
| dfn_stack[dfn_depth - 1].sym = sym; |
| // Mark the input sym as busy |
| sym->cg.top_order = DFN_BUSY; |
| |
| /* Mimic time. */ |
| DBG (LOOKUPDEBUG, |
| printf (">>> Run for:%s ticks:%lf\n", sym->name, sym->hist.time)); |
| DBG (LOOKUPDEBUG, printf (">>> Call chain:")); |
| pc_size = 0; |
| for (int i = (dfn_depth - 1); i >= 0; i--) |
| { |
| Sym *t = dfn_stack[i].sym; |
| ++pc_size; |
| if (pc_size >= pc_maxdepth) |
| { |
| pc_maxdepth += INC_DEPTH; |
| pc_array = (unsigned long *) |
| xrealloc (pc_array, pc_maxdepth * sizeof (unsigned long)); |
| } |
| pc_array[pc_size - 1] = t->addr; |
| DBG (LOOKUPDEBUG, printf ("----> %s: 0x%lx (%lf)\n", t->name, |
| t->addr, t->hist.time)); |
| } |
| DBG (LOOKUPDEBUG, printf ("\n")); |
| ClockPacket pckt; |
| memset (&pckt, 0, sizeof (ClockPacket)); |
| pckt.comm.type = CLOCK_TYPE; |
| pckt.comm.tsize = sizeof (ClockPacket); |
| pckt.lwp_id = 0xdeadbeef; |
| pckt.thr_id = 0xcafecafe; |
| pckt.cpu_id = 0; |
| pckt.tstamp = mytime++; |
| // Output `data.frameinfo` packet |
| CM_Array array; |
| array.length = pc_size * sizeof (unsigned long); |
| array.bytes = (void *) pc_array; |
| |
| pckt.frinfo = get_frame_info (&array); |
| |
| pckt.mstate = LMS_LINUX_CPU; |
| int calls = sym->ncalls > 0 ? sym->ncalls : 1; |
| double time = sym->hist.time > 0 ? sym->hist.time : 1; |
| uint64_t nticks = (uint64_t)(time / calls); |
| pckt.nticks = nticks > 0 ? nticks : 1; |
| |
| // Output `profile` packet |
| writeBufferToFile ((CM_Packet*) &pckt, SP_PROFILE_FILE); |
| } |
| |
| /* Take the last symbol out of the stack. */ |
| static void |
| post_visit (Sym *sym) |
| { |
| sym->cg.top_order = DFN_NAN; |
| if (dfn_depth) |
| --dfn_depth; |
| } |
| |
| static void |
| dfn (Sym *parent) |
| { |
| /* Detect cycles. */ |
| if (parent->cg.top_order == DFN_BUSY) |
| { |
| parent->ncalls = 1; |
| pre_visit (parent); |
| return; |
| } |
| |
| pre_visit (parent); |
| for (Arc *arc = parent->cg.children; arc; arc = arc->next_child) |
| { |
| dfn (arc->child); |
| } |
| post_visit(parent); |
| } |
| |
| /* Go through all the symbols, start from the top of a call chain. |
| Mimimc a stack trace using a DF algorithm. */ |
| |
| static int |
| cg_traverse_arcs (const char *whoami) |
| { |
| Sym *sym; |
| Sym_Table *symtab = get_symtab (whoami); |
| |
| for (sym = symtab->base; sym < symtab->limit; sym++) |
| { |
| /* Start DF algorithm from the top symbol. */ |
| if (sym->cg.parents) |
| continue; |
| /* Skip unused symbols. */ |
| if (!sym->cg.children) |
| continue; |
| dfn (sym); |
| } |
| return pc_size; |
| } |
| |
| static void |
| writeBufferToFile (const CM_Packet *pckt, |
| const char *filename) |
| { |
| size_t size = pckt->tsize; |
| size_t asz; |
| char *buffer = (char *) pckt; |
| char *new_file_path; |
| FILE *outFile; |
| |
| asz = strlen (base_folder) + strlen (filename) + 1 + 1; |
| new_file_path = (char *) alloca (asz); |
| snprintf (new_file_path, asz, "%s/%s", base_folder, filename); |
| |
| // Open the file in binary mode |
| outFile = fopen (new_file_path, "ab"); |
| if (!outFile) |
| { |
| perror ("Fopen failed"); |
| exit (1); |
| } |
| |
| // Write the buffer to the file |
| fwrite (buffer, size, 1, outFile); |
| |
| fclose (outFile); |
| } |
| |
| static uint64_t |
| compute_uid (Frame_packet *frp) |
| { |
| uint64_t idxs[LAST_INFO]; |
| uint64_t uid = ROOT_UID; |
| uint64_t idx = ROOT_IDX; |
| |
| Common_info *cinfo = (Common_info*) ((char*) frp + frp->hsize); |
| char *end = (char*) frp + frp->tsize; |
| for (;;) |
| { |
| if ((char*) cinfo >= end || cinfo->hsize == 0 || |
| (char*) cinfo + cinfo->hsize > end) |
| break; |
| |
| /* Start with a different value to avoid matching with uid */ |
| uint64_t uidt = 1; |
| uint64_t idxt = 1; |
| long *ptr = (long*) ((char*) cinfo + cinfo->hsize); |
| long *bnd = (long*) ((char*) cinfo + sizeof (Common_info)); |
| DBG (SAMPLEDEBUG, |
| printf( "compute_uid: Cnt=%ld: ", (long) cinfo->hsize)); |
| while (ptr > bnd) |
| { |
| long val = *(--ptr); |
| DBG (SAMPLEDEBUG, |
| printf ("0x%8.8llx ", (unsigned long long) val)); |
| uidt = (uidt + val) * ROOT_UID; |
| idxt = (idxt + val) * ROOT_IDX; |
| uid = (uid + val) * ROOT_UID; |
| idx = (idx + val) * ROOT_IDX; |
| } |
| if (cinfo->kind == STACK_INFO) |
| { |
| cinfo->uid = uidt; |
| idxs[cinfo->kind] = idxt; |
| } |
| cinfo = (Common_info*) ((char*) cinfo + cinfo->hsize); |
| } |
| DBG (SAMPLEDEBUG, printf ("\n")); |
| |
| /* Check if we have already recorded that uid. |
| * The following fragment contains benign data races. |
| * It's important, though, that all reads from UIDTable |
| * happen before writes. |
| */ |
| int found1 = 0; |
| int idx1 = (int) ((idx >> 44) % UIDTableSize); |
| if (UIDTable[idx1] == uid) |
| found1 = 1; |
| int found2 = 0; |
| int idx2 = (int) ((idx >> 24) % UIDTableSize); |
| if (UIDTable[idx2] == uid) |
| found2 = 1; |
| int found3 = 0; |
| int idx3 = (int) ((idx >> 4) % UIDTableSize); |
| if (UIDTable[idx3] == uid) |
| found3 = 1; |
| if (!found1) |
| UIDTable[idx1] = uid; |
| if (!found2) |
| UIDTable[idx2] = uid; |
| if (!found3) |
| UIDTable[idx3] = uid; |
| |
| if (found1 || found2 || found3) |
| { |
| return uid; |
| } |
| frp->uid = uid; |
| |
| /* Compress info's */ |
| cinfo = (Common_info*) ((char*) frp + frp->hsize); |
| for (;;) |
| { |
| if ((char*) cinfo >= end || cinfo->hsize == 0 || |
| (char*) cinfo + cinfo->hsize > end) |
| break; |
| if (cinfo->kind == STACK_INFO || cinfo->kind == JAVA_INFO) |
| { |
| long *ptr = (long*) ((char*) cinfo + sizeof (Common_info)); |
| long *bnd = (long*) ((char*) cinfo + cinfo->hsize); |
| uint64_t uidt = cinfo->uid; |
| uint64_t idxt = idxs[cinfo->kind]; |
| int found = 0; |
| int first = 1; |
| while (ptr < bnd - 1) |
| { |
| int idx1 = (int) ((idxt >> 44) % UIDTableSize); |
| if (UIDTable[idx1] == uidt) |
| { |
| found = 1; |
| break; |
| } |
| else if (first) |
| { |
| first = 0; |
| UIDTable[idx1] = uidt; |
| } |
| long val = *ptr++; |
| uidt = uidt * ROOT_UID_INV - val; |
| idxt = idxt * ROOT_IDX_INV - val; |
| } |
| if (found) |
| { |
| char *d = (char*) ptr; |
| char *s = (char*) bnd; |
| if (!first) |
| { |
| int i; |
| for (i = 0; i < (int) sizeof (uidt); i++) |
| { |
| *d++ = (char) uidt; |
| uidt = uidt >> 8; |
| } |
| } |
| int delta = s - d; |
| while (s < end) |
| *d++ = *s++; |
| cinfo->kind |= COMPRESSED_INFO; |
| cinfo->hsize -= delta; |
| frp->tsize -= delta; |
| end -= delta; |
| } |
| } |
| cinfo = (Common_info*) ((char*) cinfo + cinfo->hsize); |
| } |
| writeBufferToFile ((CM_Packet*) frp, "data.frameinfo"); |
| return uid; |
| } |
| |
| static uint64_t |
| get_frame_info (const CM_Array *array) |
| { |
| int max_native_nframes = DEFAULT_MAX_NFRAMES; |
| |
| if (array == NULL || array->length <= 0) |
| return 0; |
| |
| int max_frame_size = OVERHEAD_BYTES + NATIVE_FRAME_BYTES (max_native_nframes); |
| Frame_packet *frpckt = (Frame_packet *) alloca (sizeof (Frame_packet) + max_frame_size); |
| frpckt->type = FRAME_PCKT; |
| frpckt->hsize = sizeof (Frame_packet); |
| |
| char *d = (char*) (frpckt + 1); |
| int size = max_frame_size; |
| |
| /* create a stack image from user data */ |
| Stack_info *sinfo = (Stack_info*) d; |
| int sz = sizeof (Stack_info); |
| d += sz; |
| size -= sz; |
| sz = array->length; |
| if (sz > size) |
| sz = size; // YXXX should we mark this with truncation frame? |
| memcpy (d, array->bytes, sz); |
| d += sz; |
| size -= sz; |
| sinfo->kind = STACK_INFO; |
| sinfo->hsize = (d - (char*) sinfo); |
| |
| /* Compute the total size */ |
| frpckt->tsize = d - (char*) frpckt; |
| return compute_uid (frpckt); |
| } |
| |
| /* Generate the log.xml file. */ |
| static void |
| gen_gmon_log (void) |
| { |
| FILE *logx; |
| char *new_file_path; |
| size_t asz = strlen (base_folder) + strlen (SP_LOG_FILE) + 1 + 1; |
| |
| new_file_path = (char *) alloca (asz); |
| snprintf (new_file_path, asz, "%s/%s", base_folder, SP_LOG_FILE); |
| |
| logx = fopen (new_file_path, "w"); |
| |
| long hz = hist_get_hz (); |
| int gmon_interval = (hz == HZ_WRONG ? 1000 : hz * 100); |
| |
| fprintf (logx, "<profile name=\"%s\" ptimer=\"%d\" numstates=\"%d\">\n", |
| SP_JCMD_PROFILE, gmon_interval, LMS_MAGIC_ID_LINUX); |
| fprintf (logx, " <profdata fname=\"profile\"/>\n"); |
| |
| /* Record Profile packet description */ |
| fprintf (logx, " <profpckt kind=\"%d\" uname=\"Clock profiling data\">\n", |
| CLOCK_TYPE); |
| fprintf (logx, " <field name=\"LWPID\" uname=\"Lightweight process id\" \ |
| offset=\"%d\" type=\"%s\"/>\n", |
| (int) offsetof (ClockPacket, lwp_id), |
| fld_sizeof (ClockPacket, lwp_id) == 4 ? "INT32" : "INT64"); |
| fprintf (logx, " <field name=\"THRID\" uname=\"Thread number\" \ |
| offset=\"%d\" type=\"%s\"/>\n", |
| (int) offsetof (ClockPacket, thr_id), |
| fld_sizeof (ClockPacket, thr_id) == 4 ? "INT32" : "INT64"); |
| fprintf (logx, " <field name=\"CPUID\" uname=\"CPU id\" offset=\"%d\" \ |
| type=\"%s\"/>\n", |
| (int) offsetof (ClockPacket, cpu_id), |
| fld_sizeof (ClockPacket, cpu_id) == 4 ? "INT32" : "INT64"); |
| fprintf (logx, " <field name=\"TSTAMP\" uname=\"High resolution \ |
| timestamp\" offset=\"%d\" type=\"%s\"/>\n", |
| (int) offsetof (ClockPacket, tstamp), |
| fld_sizeof (ClockPacket, tstamp) == 4 ? "INT32" : "INT64"); |
| fprintf (logx, " <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n", |
| (int) offsetof (ClockPacket, frinfo), |
| fld_sizeof (ClockPacket, frinfo) == 4 ? "INT32" : "INT64"); |
| fprintf (logx, " <field name=\"MSTATE\" uname=\"Thread state\" \ |
| offset=\"%d\" type=\"%s\"/>\n", |
| (int) offsetof (ClockPacket, mstate), |
| fld_sizeof (ClockPacket, mstate) == 4 ? "INT32" : "INT64"); |
| fprintf (logx, " <field name=\"NTICK\" uname=\"Duration\" offset=\"%d\" \ |
| type=\"%s\"/>\n", |
| (int) offsetof (ClockPacket, nticks), |
| fld_sizeof (ClockPacket, nticks) == 4 ? "INT32" : "INT64"); |
| fprintf (logx, " </profpckt>\n"); |
| fprintf (logx,"</profile>\n"); |
| fprintf (logx, "<event kind=\"%s\" tstamp=\"%u.%09u\" time=\"%lld\" \ |
| tm_zone=\"%lld\"/>\n", |
| SP_JCMD_RUN, 0, 0, 0LL, 0LL); |
| |
| fclose (logx); |
| } |
| |
| /* This will not work for PIE execs. */ |
| |
| static void |
| gen_gmon_map (char *name) |
| { |
| bfd_vma mpage, page_size = sysconf(_SC_PAGESIZE); |
| bfd_vma loadaddr, vaddr = core_text_sect->vma; //lma? |
| bfd_size_type msize = core_text_sect->size; |
| int timestamp = 1; |
| int offset = 0; |
| int check = -1; |
| int modeflags = PROT_READ | PROT_EXEC; //0x05 |
| char *new_file_path; |
| size_t asz = strlen (base_folder) + strlen (SP_MAP_FILE) + 1 + 1; |
| |
| new_file_path = (char *) alloca (asz); |
| snprintf (new_file_path, asz, "%s/%s", base_folder, SP_MAP_FILE); |
| |
| FILE *mapx = fopen (new_file_path, "w"); |
| |
| if (mapx == NULL) { |
| return; |
| } |
| /* alignment mask. */ |
| mpage = ~(page_size - 1); |
| // Round down to page size alignment |
| loadaddr = vaddr & mpage; |
| // Compensate for the alignment gain |
| msize += vaddr - loadaddr; |
| // Round up to a multiple of page size; |
| msize = (msize + page_size - 1) & mpage; |
| |
| fprintf (mapx, "<event kind=\"map\" object=\"segment\" tstamp=\"%u.%09u\" " |
| "vaddr=\"0x%016llX\" size=\"%llu\" pagesz=\"%d\" foffset=\"%c0x%08llX\" " |
| "modes=\"0x%03X\" chksum=\"0x%0X\" name=\"%s\"/>\n", |
| (unsigned) (timestamp / NANOSEC), |
| (unsigned) (timestamp % NANOSEC), |
| (long long unsigned) loadaddr, (long long unsigned) msize, |
| (int) page_size, offset < 0 ? '-' : '+', |
| (long long unsigned) (offset < 0 ? -offset : offset), |
| modeflags, check, name); |
| |
| fclose (mapx); |
| } |
| |
| static int |
| checkflagterm (const char *c) |
| { |
| if (c[2] != 0) |
| { |
| dbe_write (2, GTXT ("gmon: unrecognized argument `%s'\n"), c); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static void |
| writeStr (int f, const char *buf) |
| { |
| if (buf != NULL) |
| write (f, buf, strlen (buf)); |
| } |
| |
| /* main entry point. It calls catch_out_of_memory which will call |
| real_main() function. */ |
| int |
| main (int argc, char *argv[]) |
| { |
| xmalloc_set_program_name (argv[0]); |
| return catch_out_of_memory (real_main, argc, argv); |
| } |
| |
| er_gmon::er_gmon (int argc, char *argv[]) : DbeApplication (argc, argv) |
| { |
| int sz = UIDTableSize * sizeof (*UIDTable); |
| UIDTable = (uint64_t *) xmalloc (sz); |
| if (!UIDTable) |
| return; |
| memset (UIDTable, 0, sz); |
| overwrite = false; |
| cc = NULL; |
| } |
| |
| void |
| er_gmon::start (int argc, char *argv[]) |
| { |
| char *gmon_name; |
| char *a_out_name; |
| // Create a collector control structure, disabling aggressive |
| // warning. |
| cc = new Coll_Ctrl (0, false, false); |
| cc->set_default_stem ("gmon"); |
| |
| whoami = argv[0]; |
| if (argc > 1 && strncmp (argv[1], NTXT ("--whoami="), 9) == 0) |
| { |
| whoami = argv[1] + 9; |
| argc--; |
| argv++; |
| } |
| if (argc == 2 && strcmp (argv[1], NTXT ("-h")) == 0) |
| { |
| /* only one argument, -h */ |
| usage (); |
| } |
| else if (argc == 2 && (strcmp (argv[1], NTXT ("-help")) == 0 || |
| strcmp (argv[1], NTXT ("--help")) == 0)) |
| { |
| /* only one argument, -help or --help */ |
| usage (); |
| } |
| else if ((argc == 2) && |
| (strcmp (argv[1], NTXT ("--version")) == 0)) |
| { |
| /* only one argument, --version */ |
| |
| /* print the version info */ |
| Application::print_version_info (); |
| exit (0); |
| } |
| |
| int adj = check_mods (argc, argv); |
| if (adj < 0) |
| { |
| usage_and_exit (1); |
| } |
| |
| char *ret = cc->create_exp_dir (); |
| if (ret != NULL) |
| { |
| dbe_write (2, NTXT ("%s\n"), ret); |
| free (ret); |
| exit (1); |
| } |
| |
| // Get the file path. |
| base_folder = cc->get_experiment (); |
| |
| // Default names |
| a_out_name = xstrdup ("a.out"); |
| gmon_name = xstrdup ("gmon.out"); |
| // Get the ELF and the GMON.OUT file if they exist. |
| if (argc == (adj + 1)) |
| { |
| a_out_name = argv[adj]; |
| } |
| else if (argc == (adj + 2)) |
| { |
| a_out_name = argv[adj++]; |
| gmon_name = argv[adj]; |
| } |
| else if (argc != adj) |
| { |
| usage_and_exit (1); |
| } |
| |
| /* Read the elf syms and the gmon file. */ |
| if (core_init (a_out_name, whoami) < 0) |
| { |
| cc->remove_exp_dir (); |
| exit (1); |
| } |
| if (gmon_out_read (gmon_name, FF_AUTO, whoami) < 0) |
| { |
| cc->remove_exp_dir (); |
| exit (1); |
| } |
| |
| /* Process the gmon file, and output the gprofng project files. */ |
| hist_assign_samples (whoami); |
| cg_traverse_arcs (whoami); |
| gen_gmon_map (a_out_name); |
| gen_gmon_log (); |
| } |
| |
| /* Get the args and search for modifiers. */ |
| int |
| er_gmon::check_mods (int argc, char *argv[]) |
| { |
| char *expName = NULL; |
| int i = -1; |
| for (i = 1; i < argc; i++) |
| { |
| if (argv[i] == NULL) |
| break; |
| if (argv[i][0] != '-') |
| break; |
| switch (argv[i][1]) |
| { |
| case 'O': |
| overwrite = true; |
| //FALLTHROU |
| case 'o': |
| if (checkflagterm (argv[i]) == -1) |
| return -1; |
| if (argv[i + 1] == NULL) |
| { |
| dbe_write (2, |
| GTXT ("Argument %s must be followed by a file name\n"), |
| argv[i]); |
| return -1; |
| } |
| if (expName != NULL) |
| { |
| dbe_write (2, GTXT ("Only one -o or -O argument may be used\n")); |
| return -1; |
| } |
| expName = argv[i + 1]; |
| i++; |
| break; |
| |
| default: |
| dbe_write (2, GTXT ("gmon: unrecognized argument `%s'\n"), |
| argv[i]); |
| return -1; |
| } |
| } |
| if (expName) |
| { |
| char *ccret; |
| char *ccwarn = NULL; |
| ccret = cc->set_expt (expName, &ccwarn, overwrite); |
| if (ccwarn) |
| { |
| writeStr (2, ccwarn); |
| free (ccwarn); |
| } |
| if (ccret) |
| { |
| writeStr (2, ccret); |
| return -1; |
| } |
| } |
| return i; |
| } |
| |
| void |
| er_gmon::usage () |
| { |
| usage_and_exit (0); |
| } |
| |
| void |
| er_gmon::usage_and_exit (int exit_code) |
| { |
| printf ( GTXT ( |
| "Usage: gprofng display gmon [OPTION(S)] [TARGET-OBJECT [GMON-FILE]]\n")); |
| printf ( GTXT ( |
| "\n" |
| "Convert an gmon.out file to a gprofng experiment.\n" |
| "\n" |
| "Options:\n" |
| "\n" |
| " --version print the version number and exit.\n" |
| " -h/--help print usage information and exit.\n" |
| "\n" |
| " -o <exp_name> specify the name for (and path to) the experiment directory; the\n" |
| " the default path is the current directory.\n" |
| "\n" |
| " -O <exp_name> the same as -o, but unlike the -o option, silently overwrite an\n" |
| " existing experiment directory with the same name.\n" |
| "\n")); |
| |
| exit (exit_code); |
| } |
| |
| er_gmon::~er_gmon () |
| { |
| free (UIDTable); |
| delete cc; |
| } |