diff options
author | Vladimir Mezentsev <vladimir.mezentsev@oracle.com> | 2022-03-11 08:58:31 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2022-03-11 08:58:31 +0000 |
commit | bb368aad297fe3ad40cf397e6fc85aa471429a28 (patch) | |
tree | 0ab25909b8fe789d676bbdb00d501d4d485e4afe /gprofng/src/parse.cc | |
parent | a655f19af95eb685ba64f48ee8fc2b3b7a3d886a (diff) | |
download | gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.zip gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.gz gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.bz2 |
gprofng: a new GNU profiler
top-level
* Makefile.def: Add gprofng module.
* configure.ac: Add --enable-gprofng option.
* src-release.sh: Add gprofng.
* Makefile.in: Regenerate.
* configure: Regenerate.
* gprofng: New directory.
binutils
* MAINTAINERS: Add gprofng maintainer.
* README-how-to-make-a-release: Add gprofng.
include.
* collectorAPI.h: New file.
* libcollector.h: New file.
* libfcollector.h: New file.
Diffstat (limited to 'gprofng/src/parse.cc')
-rw-r--r-- | gprofng/src/parse.cc | 927 |
1 files changed, 927 insertions, 0 deletions
diff --git a/gprofng/src/parse.cc b/gprofng/src/parse.cc new file mode 100644 index 0000000..ab22270 --- /dev/null +++ b/gprofng/src/parse.cc @@ -0,0 +1,927 @@ +/* Copyright (C) 2021 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 <sys/param.h> +#include <sys/mman.h> + +#include "util.h" +#include "DbeFile.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "Emsg.h" +#include "Function.h" +#include "LoadObject.h" +#include "Module.h" +#include "PRBTree.h" +#include "Sample.h" +#include "Elf.h" + +void +Experiment::mrec_insert (MapRecord *mrec) +{ + int sz = mrecs->size (); + MapRecord *tmp = sz > 0 ? mrecs->fetch (sz - 1) : NULL; + + // The following should work in most cases + if (tmp == NULL || tmp->ts <= mrec->ts) + { + mrecs->append (mrec); + return; + } + + // If it didn't... + int lo = 0; + int hi = sz - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + tmp = mrecs->fetch (md); + if (tmp->ts < mrec->ts) + lo = md + 1; + else + hi = md - 1; + } + mrecs->insert (lo, mrec); +} + +int +Experiment::process_arglist_cmd (char *, char *arglist) +{ + uarglist = arglist; + + // find argv[0], and extract its basename + if (strcmp (uarglist, NTXT ("(fork)")) == 0) + return 0; // leaving target name NULL + char *p = uarglist; + char *pp = uarglist; + char *pl; + for (;;) + { + if (*p == '/') + pp = p + 1; + if (*p == ' ' || *p == 0) + { + pl = p; + break; + } + p++; + } + size_t len = pl - pp; + if (len > 0) + utargname = dbe_sprintf (NTXT ("%.*s"), (int) len, pp); + return 0; +} + +int +Experiment::process_desc_start_cmd (char *, hrtime_t ts, char *flavor, + char *nexp, int follow, char *txt) +{ + char *str; + Emsg *m; + + if (follow == 1) + str = dbe_sprintf (GTXT ("Starting %s %ld.%09ld, exp %s.er, \"%s\""), + flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC), + nexp, txt); + else + str = dbe_sprintf (GTXT ("Starting %s %ld.%09ld, no experiment, \"%s\""), + flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC), + txt); + m = new Emsg (CMSG_COMMENT, str); + free (str); + runlogq->append (m); + + free (flavor); + free (nexp); + free (txt); + return 0; +} + +int +Experiment::process_desc_started_cmd (char *, hrtime_t ts, char *flavor, + char *nexp, int follow, char *txt) +{ + char *str; + Emsg *m; + + if (follow == 1) + str = dbe_sprintf (GTXT ("Started %s %ld.%09ld, exp %s.er, \"%s\""), + flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC), + nexp, txt); + else + str = dbe_sprintf (GTXT ("Started %s %ld.%09ld, no experiment, \"%s\""), + flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC), + txt); + m = new Emsg (CMSG_COMMENT, str); + free (str); + runlogq->append (m); + free (flavor); + free (nexp); + free (txt); + return 0; +} + +LoadObject * +Experiment::get_dynfunc_lo (const char *loName) +{ + LoadObject *lo = loadObjMap->get (loName); + if (lo == NULL) + { + lo = createLoadObject (loName, expIdx);// DYNFUNC_SEGMENT is always unique + lo->dbeFile->filetype |= DbeFile::F_FICTION; + lo->flags |= SEG_FLAG_DYNAMIC; + lo->type = LoadObject::SEG_TEXT; + lo->set_platform (platform, wsize); + append (lo); + } + return lo; +} + +Function * +Experiment::create_dynfunc (Module *mod, char *fname, int64_t vaddr, + int64_t fsize) +{ + Function *f = dbeSession->createFunction (); + f->set_name (fname); + f->flags |= FUNC_FLAG_DYNAMIC; + f->size = fsize; + f->img_offset = vaddr; + f->module = mod; + mod->functions->append (f); + mod->loadobject->functions->append (f); + return f; +} + +static int +func_cmp (const void *a, const void *b) +{ + Function *fp1 = *((Function **) a); + Function *fp2 = *((Function **) b); + uint64_t i1 = fp1->img_offset; + uint64_t i2 = fp2->img_offset; + return i1 < i2 ? -1 : i1 == i2 ? 0 : 1; +} + +int +Experiment::process_fn_load_cmd (Module *mod, char *fname, Vaddr vaddr, + int fsize, hrtime_t ts) +{ + Dprintf (DEBUG_MAPS, + "process_fn_load_cmd:%s (%s) vaddr=0x%llx msize=%lld ts=%lld\n", + STR (mod ? mod->get_name () : NULL), STR (fname), + (unsigned long long) vaddr, (long long) fsize, (long long) ts); + if (mod != NULL) + { + mod->functions->sort (func_cmp); + uint64_t lastVaddr = vaddr; + for (int i = 0, sz = mod->functions->size (); i < sz; i++) + { + Function *f = mod->functions->fetch (i); + if (lastVaddr < f->img_offset) + { + char *fnm = dbe_sprintf (GTXT ("<static>@0x%llx (%s)"), + (unsigned long long) lastVaddr, fname); + create_dynfunc (mod, fnm, lastVaddr, f->img_offset - lastVaddr); + free (fnm); + } + lastVaddr = f->img_offset + f->size; + } + if (lastVaddr < vaddr + fsize) + { + char *fnm = dbe_sprintf (GTXT ("<static>@0x%llx (%s)"), + (unsigned long long) lastVaddr, fname); + create_dynfunc (mod, fnm, lastVaddr, vaddr + fsize - lastVaddr); + free (fnm); + } + mod->functions->sort (func_cmp); + for (int i = 0, sz = mod->functions->size (); i < sz; i++) + { + Function *f = mod->functions->fetch (i); + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = f; + mrec->base = f->img_offset; + mrec->size = f->size; + mrec->ts = ts; + mrec->foff = 0; + mrec_insert (mrec); + } + return 0; + } + + LoadObject *ds = get_dynfunc_lo (DYNFUNC_SEGMENT); + Function *dfunc = create_dynfunc (ds->noname, fname, vaddr, fsize); + + // check for special functions, USER, IDLE, TRUNC to disable offsets in disassembly + // XXX -- check based on name now + // Optimization: use pre-initialized localized strings + static const char * localized_USER_MODE = NULL; + static const char * localized_IDLE = NULL; + static const char * localized_TRUNCATED_STACK = NULL; + if (localized_USER_MODE == NULL) + { + localized_USER_MODE = GTXT ("<USER_MODE>"); + localized_IDLE = GTXT ("<IDLE>"); + localized_TRUNCATED_STACK = GTXT ("<TRUNCATED_STACK>"); + } + if (strcmp (fname, localized_USER_MODE) == 0 + || strcmp (fname, localized_IDLE) == 0 + || strcmp (fname, localized_TRUNCATED_STACK) == 0) + dfunc->flags |= FUNC_FLAG_NO_OFFSET; + + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = dfunc; + mrec->base = vaddr; + mrec->size = fsize; + mrec->ts = ts; + mrec->foff = 0; + mrec_insert (mrec); + return 0; +} + +int +Experiment::process_fn_unload_cmd (char *, Vaddr vaddr, hrtime_t ts) +{ + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::UNLOAD; + mrec->base = vaddr; + mrec->ts = ts; + mrec_insert (mrec); + return 0; +} + +void +Experiment::register_metric (Metric::Type type) +{ + BaseMetric *mtr = dbeSession->register_metric (type); + metrics->append (mtr); +} + +void +Experiment::register_metric (Hwcentry *ctr, const char* aux, const char* uname) +{ + BaseMetric *mtr = dbeSession->register_metric (ctr, aux, uname); + metrics->append (mtr); + if (mtr->get_dependent_bm ()) + metrics->append (mtr->get_dependent_bm ()); +} + +int +Experiment::process_hwcounter_cmd (char *, int cpuver, char *counter, + char * int_name, int interval, int tag, + int i_tpc, char *modstr) +{ + char *str; + Emsg *m; + Hwcentry *ctr; + ABST_type tpc = (ABST_type) i_tpc; + + // Use previously ignored tag to associate counter packets. + if (tag < 0 || tag >= MAX_HWCOUNT) + { + // invalid tag specified, warn user + str = dbe_sprintf (GTXT ("*** Error: HW counter tag %d out of range [%d - %d]; ignored"), + tag, 0, MAX_HWCOUNT - 1); + m = new Emsg (CMSG_ERROR, str); + free (str); + errorq->append (m); + free (counter); + return 0; + } + if (coll_params.hw_aux_name[tag]) + { + // duplicate tag used, warn user + str = dbe_sprintf (GTXT ("*** Error: Duplicate HW counter tag %d specified; ignored"), + tag); + m = new Emsg (CMSG_ERROR, str); + free (str); + errorq->append (m); + free (counter); + return 0; + } + hw_cpuver = cpuver; + + // map it to a machinemodel string + if (hw_cpuver != CPUVER_UNDEFINED) + { + free (machinemodel); + if (hw_cpuver == 1104) + machinemodel = dbe_strdup (NTXT ("t4")); + else if (hw_cpuver == 1110) + machinemodel = dbe_strdup (NTXT ("t5")); + else if (hw_cpuver == 1204) + machinemodel = dbe_strdup (NTXT ("m4")); + else if (hw_cpuver == 1210) + machinemodel = dbe_strdup (NTXT ("m5")); + else if (hw_cpuver == 1220) + machinemodel = dbe_strdup (NTXT ("m6")); + else if (hw_cpuver == 1230) + machinemodel = dbe_strdup (NTXT ("m7")); + else + machinemodel = dbe_strdup (NTXT ("generic")); + } + + // Find the entry in the machine table, and dup it + ctr = new Hwcentry; + dbeSession->append (ctr); + hwc_post_lookup (ctr, counter, int_name, cpuver); + ctr->sort_order = tag; + ctr->memop = tpc; + + // Check if HWC name is to be modified + if (modstr != NULL) + { + char *s = ctr->name; + ctr->name = dbe_sprintf (NTXT ("%s%s"), modstr, s); + s = ctr->int_name; + ctr->int_name = dbe_sprintf (NTXT ("%s%s"), modstr, s); + s = ctr->metric; + if (s) + ctr->metric = dbe_sprintf (NTXT ("%s%s"), modstr, s); + } + + char * cname = dbe_strdup (ctr->name); + char * uname = dbe_strdup (hwc_i18n_metric (ctr)); + coll_params.hw_aux_name[tag] = cname; + coll_params.hw_username[tag] = uname; + coll_params.hw_interval[tag] = interval; + coll_params.hw_tpc[tag] = tpc; + coll_params.hw_cpu_ver[tag] = cpuver; + + // set hw_mode and xhw_mode? + coll_params.hw_mode = 1; + if (ABST_MEMSPACE_ENABLED (tpc)) + { + // yes, dataspace data available + coll_params.xhw_mode = 1; + + // set dataspace available + dataspaceavail = true; + } + register_metric (ctr, cname, uname); + free (counter); + return 0; +} + +// TBR:? + +int +Experiment::process_hwsimctr_cmd (char *, int cpuver, char *nm, char *int_name, + char *metric, int reg, + int interval, int timecvt, int i_tpc, int tag) +{ + char *str; + Emsg *m; + Hwcentry *ctr; + ABST_type tpc = (ABST_type) i_tpc; + + // Use previously ignored tag to associate counter packets. + if (tag < 0 || tag >= MAX_HWCOUNT) + { + // invalid tag specified, warn user + str = dbe_sprintf (GTXT ("*** Error: HW counter tag %d out of range [%d - %d]; ignored"), + tag, 0, MAX_HWCOUNT - 1); + m = new Emsg (CMSG_ERROR, str); + free (str); + errorq->append (m); + + free (nm); + free (int_name); + free (metric); + return 0; + } + if (coll_params.hw_aux_name[tag]) + { + // duplicate tag used, warn user + str = dbe_sprintf (GTXT ("*** Error: Duplicate HW counter tag %d specified; ignored"), + tag); + m = new Emsg (CMSG_ERROR, str); + free (str); + errorq->append (m); + free (nm); + free (int_name); + free (metric); + return 0; + } + hw_cpuver = cpuver; + ctr = new Hwcentry; + { + static Hwcentry empty; + *ctr = empty; + } + ctr->name = nm; + ctr->int_name = int_name; + ctr->metric = metric; + ctr->reg_num = reg; + ctr->val = interval; + ctr->timecvt = timecvt; + ctr->memop = tpc; + ctr->sort_order = tag; + + char *cname = dbe_strdup (ctr->name); + char *uname = dbe_strdup (hwc_i18n_metric (ctr)); + + coll_params.hw_aux_name[tag] = cname; + coll_params.hw_username[tag] = uname; + coll_params.hw_interval[tag] = interval; + coll_params.hw_tpc[tag] = tpc; + coll_params.hw_cpu_ver[tag] = cpuver; + + // set hw_mode and xhw_mode? + coll_params.hw_mode = 1; + if (ABST_MEMSPACE_ENABLED (tpc)) + { + coll_params.xhw_mode = 1; + // set dataspace available + if (getenv ("ANALYZER_DATASPACE_COUNT") != 0) + dataspaceavail = true; + } + + register_metric (ctr, cname, uname); + return 0; +} + +int +Experiment::process_jcm_load_cmd (char *, Vaddr mid, Vaddr vaddr, + int msize, hrtime_t ts) +{ + if (jmaps == NULL) + return 1; + + JMethod *jfunc = (JMethod*) jmaps->locate_exact_match (mid, ts); + if (jfunc == NULL || jfunc->get_type () != Histable::FUNCTION) + return 1; + + LoadObject *ds = get_dynfunc_lo (JAVA_COMPILED_METHODS); + Module *jmodule = jfunc->module; + Module *dmodule = ds->noname; + if (jmodule) + { + dmodule = dbeSession->createModule (ds, jmodule->get_name ()); + dmodule->lang_code = Sp_lang_java; + dmodule->set_file_name (dbe_strdup (jmodule->file_name)); + } + + JMethod *dfunc = dbeSession->createJMethod (); + dfunc->flags |= FUNC_FLAG_DYNAMIC; + dfunc->size = msize; + dfunc->module = dmodule; + dfunc->usrfunc = jfunc; + dfunc->set_addr (vaddr); + dfunc->set_mid (mid); + dfunc->set_signature (jfunc->get_signature ()); + dfunc->set_name (jfunc->get_mangled_name ()); + ds->functions->append (dfunc); + dmodule->functions->append (dfunc); + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = dfunc; + mrec->base = vaddr; + mrec->size = msize; + mrec->ts = ts; + mrec->foff = 0; + mrec_insert (mrec); + return 0; +} + +int +Experiment::process_jcm_unload_cmd (char *, Vaddr /*mid*/, hrtime_t /*ts*/) +{ + if (jmaps == NULL) + return 1; + + // We are ignoring this record because of the flaw in + // JVMPI desing that doesn't distinguish between two or more + // compiled instances of a method when an unload event is + // generated: + // JVMPI_COMPILED_METHOD_LOAD( mid, addr1, ... ) + // JVMPI_COMPILED_METHOD_LOAD( mid, addr2, ... ) + // JVMPI_COMPILED_METHOD_UNLOAD( mid ) -- which one? + // We rely on the ability of the PRBTree algorithms to + // perform mapping appropriately based on timestamps. + return 0; +} + +int +Experiment::process_jthr_end_cmd (char *, uint64_t tid64, Vaddr jthr, + Vaddr jenv, hrtime_t ts) +{ + int lt = 0; + int rt = jthreads_idx->size () - 1; + uint32_t ttid = mapTagValue (PROP_THRID, tid64); + while (lt <= rt) + { + int md = (lt + rt) / 2; + JThread *jthread = jthreads_idx->fetch (md); + if (jthread->tid < ttid) + lt = md + 1; + else if (jthread->tid > ttid) + rt = md - 1; + else + { + for (; jthread; jthread = jthread->next) + { + if (jthread->jenv == jenv) + { + jthread->end = ts; + return 0; + } + } + return 0; + } + } + JThread *jthread = new JThread; + jthread->tid = mapTagValue (PROP_THRID, tid64); + jthread->jthr = jthr; + jthread->jenv = jenv; + jthread->jthr_id = jthreads->size (); + jthread->start = ZERO_TIME; + jthread->end = ts; + jthread->next = NULL; + jthreads->append (jthread); + if (lt == jthreads_idx->size ()) + jthreads_idx->append (jthread); + else + jthreads_idx->insert (lt, jthread); + return 0; +} + +int +Experiment::process_jthr_start_cmd (char *, char *thread_name, char *group_name, + char *parent_name, uint64_t tid64, + Vaddr jthr, Vaddr jenv, hrtime_t ts) +{ + JThread *jthread = new JThread; + jthread->name = thread_name; + jthread->group_name = group_name; + jthread->parent_name = parent_name; + jthread->tid = mapTagValue (PROP_THRID, tid64); + jthread->jthr = jthr; + jthread->jenv = jenv; + jthread->jthr_id = jthreads->size (); + jthread->start = ts; + jthread->end = MAX_TIME; + jthread->next = NULL; + + jthreads->append (jthread); + + int lt = 0; + int rt = jthreads_idx->size () - 1; + while (lt <= rt) + { + int md = (lt + rt) / 2; + JThread *jtmp = jthreads_idx->fetch (md); + if (jtmp->tid < jthread->tid) + lt = md + 1; + else if (jtmp->tid > jthread->tid) + rt = md - 1; + else + { + jthread->next = jtmp; + jthreads_idx->store (md, jthread); + return 0; + } + } + if (lt == jthreads_idx->size ()) + jthreads_idx->append (jthread); + else + jthreads_idx->insert (lt, jthread); + return 0; +} + +int +Experiment::process_gc_end_cmd ( + hrtime_t ts) +{ + if (gcevents->size () == 0) + { + GCEvent *gcevent = new GCEvent; + gcevent->start = ZERO_TIME; + gcevent->end = ts; + gcevent->id = gcevents->size () + 1; + gcevents->append (gcevent); + return 0; + } + GCEvent *gcevent = gcevents->fetch (gcevents->size () - 1); + if (gcevent->end == MAX_TIME) + gcevent->end = ts; + else + // Weird: gc_end followed by another gc_end + gcevent->end = ts; // extend the previous event + return 0; +} + +int +Experiment::process_gc_start_cmd ( + hrtime_t ts) +{ + if (gcevents->size () != 0) + { + GCEvent *gcevent = gcevents->fetch (gcevents->size () - 1); + // Weird: gc_start followed by another gc_start + if (gcevent->end == MAX_TIME) + return 0; // ignore nested gc_starts + } + GCEvent *gcevent = new GCEvent; + gcevent->start = ts; + gcevent->end = MAX_TIME; + gcevent->id = gcevents->size () + 1; + gcevents->append (gcevent); + return 0; +} + +int +Experiment::process_sample_cmd (char */*cmd*/, hrtime_t /*log_xml_time*/, + int sample_number, char *label) +{ + // sample 0 is not a sample but the starting point + if (sample_number == 0) + { + first_sample_label = label; + return 0; + } + Sample *prev_sample = samples->size () > 0 ? + samples->fetch (samples->size () - 1) : NULL; + char *start_lable = prev_sample ? + prev_sample->end_label : first_sample_label; + Sample *sample = new Sample (sample_number); + sample->start_label = dbe_strdup (start_lable); + sample->end_label = label; + samples->append (sample); + return 0; +} + +int +Experiment::process_sample_sig_cmd (char *, int sig) +{ + char *str; + Emsg *m; + str = dbe_sprintf (GTXT ("Sample signal %d"), sig); + m = new Emsg (CMSG_COMMENT, str); + free (str); + runlogq->append (m); + return 0; +} + +int +Experiment::process_seg_map_cmd (char */*cmd*/, hrtime_t ts, Vaddr vaddr, + int mapsize, int /*pagesize*/, int64_t offset, + int64_t modeflags, int64_t chk, char *nm) +{ + if (nm == NULL || + strncmp (nm + 1, SP_MAP_UNRESOLVABLE, strlen (SP_MAP_UNRESOLVABLE)) == 0) + return 0; + + LoadObject *lo = loadObjMap->get (nm); + if (lo == NULL) + { + if (chk == 0) + { + char *archName = checkFileInArchive (nm, false); + if (archName) + { + Elf *elf = new Elf (archName); + if (elf->status == Elf::ELF_ERR_NONE) + { + chk = elf->elf_checksum (); + } + free (archName); + delete elf; + } + } + lo = dbeSession->find_lobj_by_name (nm, chk); + if (lo == NULL) + { + // Skip non-text segments + if (modeflags != (PROT_READ | PROT_EXEC)) + return 0; + // A new segment + lo = createLoadObject (nm, chk); + if (strstr (nm, NTXT ("libjvm.so"))) + { + lo->flags |= SEG_FLAG_JVM; + // Make sure <JVM-System> is created + (void) dbeSession->get_jvm_Function (); + } + else if (strstr (nm, NTXT ("libmtsk.so"))) + { + lo->flags |= SEG_FLAG_OMP; + // Make sure all pseudo functions are created + for (int i = 0; i < OMP_LAST_STATE; i++) + (void) dbeSession->get_OMP_Function (i); + } + else if (dbe_strcmp (utargname, get_basename (nm)) == 0) + { + lo->flags |= SEG_FLAG_EXE; + (void) dbeSession->comp_lobjs->get ((char *) COMP_EXE_NAME, lo); + } + lo->checksum = chk; + // This is the default segment type + lo->type = LoadObject::SEG_TEXT; + lo->flags = lo->flags | SEG_FLAG_REORDER; + lo->set_platform (platform, wsize); + } + if (lo->dbeFile->get_location (false) == NULL) + { + char *archName = checkFileInArchive (nm, false); + if (archName) + { + lo->dbeFile->set_location (archName); + lo->dbeFile->inArchive = true; + lo->dbeFile->check_access (archName); // init 'sbuf' + lo->dbeFile->sbuf.st_mtime = 0; // Don't check timestamps + free (archName); + } + else + { + archName = checkFileInArchive (nm, true); + if (archName) + { + lo->set_archname (archName); + lo->need_swap_endian = need_swap_endian; + } + } + if (!dbeSession->archive_mode) + lo->sync_read_stabs (); + } + append (lo); + } + if (lo->size == 0) + lo->size = mapsize; + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = lo; + mrec->base = vaddr; + mrec->size = mapsize; + mrec->ts = ts; + mrec->foff = offset; + mrec_insert (mrec); + return 0; +} + +int +Experiment::process_seg_unmap_cmd (char */*cmd*/, hrtime_t ts, Vaddr vaddr) +{ + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::UNLOAD; + mrec->base = vaddr; + mrec->ts = ts; + mrec_insert (mrec); + return 0; +} + +int +Experiment::process_Linux_kernel_cmd (hrtime_t ts) +{ + LoadObject *lo = createLoadObject ("LinuxKernel"); + lo->flags |= SEG_FLAG_EXE; + lo->type = LoadObject::SEG_TEXT; + lo->set_platform (platform, wsize); + append (lo); + long long unsigned lo_min = (long long unsigned) (-1); + long long unsigned lo_max = 0; + Module *mod = dbeSession->createModule (lo, "LinuxKernel"); + /* + * XXX need to review mod initialization + * A specific issue is mod->file_name. Options include: + * *) NULL + * This leads to seg faults in, e.g., Timeline view. + * *) "/lib/modules/$(uname -r)/kernel/kernel/ctf/ctf.ko" + * This leads to garbage in the Source view. + * *) "/boot/vmlinuz-$(uname -r)" + * This cannot be parsed for DWARF and is sometimes not found, + * but the Analyzer seems to handle such problems. + * *) "LinuxKernel" + * This is not a proper file name, + * but again Analyzer handles the case of not finding the file or not reading DWARF from it. + */ + mod->set_file_name (dbe_strdup ("LinuxKernel")); + char last_mod_name[256]; + last_mod_name[0] = '\0'; + size_t line_n = 0; + char *line = NULL; + char kallmodsyms_copy[MAXPATHLEN]; + snprintf (kallmodsyms_copy, sizeof (kallmodsyms_copy), "%s/kallmodsyms", + expt_name); + FILE *fd = fopen (kallmodsyms_copy, "r"); + if (fd == NULL) + { + char *s = dbe_sprintf (GTXT ("*** Error: Cannot find kernel module symbols file %s; ignored"), + kallmodsyms_copy); + Emsg *m = new Emsg (CMSG_ERROR, s); + free (s); + errorq->append (m); + lo_min = 0; + } + else + { + while (getline (&line, &line_n, fd) > 0) + { + long long unsigned sym_addr; + long long unsigned sym_size; + char sym_type; + int sym_text; + char sym_name[256]; + char mod_name[256] = "vmlinux]"; /* note trailing ] */ + sscanf (line, "%llx %llx %c %s [%s", &sym_addr, &sym_size, &sym_type, + sym_name, mod_name); + if (line[0] == '\n' || line[0] == 0) + continue; + sym_text = (sym_type == 't' || sym_type == 'T'); + mod_name[strlen (mod_name) - 1] = '\0'; /* chop trailing ] */ + if (strcmp (mod_name, "ctf") == 0) + strcpy (mod_name, "shared_ctf"); + + char *mod_name_ptr; + int skip; +#define strstarts(var, x) (strncmp(var, x, strlen (x)) == 0) + if (strcmp (sym_name, "__per_cpu_start") == 0 + || strcmp (sym_name, "__per_cpu_end") == 0 + || strstarts (sym_name, "__crc_") + || strstarts (sym_name, "__ksymtab_") + || strstarts (sym_name, "__kcrctab_") + || strstarts (sym_name, "__kstrtab_") + || strstarts (sym_name, "__param_") + || strstarts (sym_name, "__syscall_meta__") + || strstarts (sym_name, "__p_syscall_meta__") + || strstarts (sym_name, "__event_") + || strstarts (sym_name, "event_") + || strstarts (sym_name, "ftrace_event_") + || strstarts (sym_name, "types__") + || strstarts (sym_name, "args__") + || strstarts (sym_name, "__tracepoint_") + || strstarts (sym_name, "__tpstrtab_") + || strstarts (sym_name, "__tpstrtab__") + || strstarts (sym_name, "__initcall_") + || strstarts (sym_name, "__setup_") + || strstarts (sym_name, "__pci_fixup_") + || strstarts (sym_name, "__dta_") + || strstarts (sym_name, "__dtrace_probe_") + || (strstr (sym_name, ".") != NULL + && strstr (sym_name, ".clone.") == NULL)) + { + mod_name_ptr = last_mod_name; + skip = 1; + } + else + { + mod_name_ptr = mod_name; + skip = 0; + } +#undef strstarts + + if (sym_text && skip == 0) + { + char fname[128]; + snprintf (fname, sizeof (fname), "%s`%s", mod_name_ptr, sym_name); + Function *func = dbeSession->createFunction (); + func->set_name (fname); + // func->flags |= FUNC_FLAG_???; // XXX + func->size = sym_size; + func->img_offset = sym_addr; + func->module = mod; + lo->functions->append (func); + mod->functions->append (func); + if (lo_min > sym_addr) + lo_min = sym_addr; + if (lo_max < sym_addr + sym_size) + lo_max = sym_addr + sym_size; + } + sprintf (last_mod_name, mod_name_ptr); + } + fclose (fd); + } + free (line); + lo->size = lo_max; + lo->functions->sort (func_cmp); + mod->functions->sort (func_cmp); + + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = lo; + mrec->base = lo_min; + mrec->size = lo_max - lo_min; + mrec->ts = ts; + mrec->foff = lo_min; + mrec_insert (mrec); + return 0; +} |