mirror of
https://github.com/autc04/Retro68.git
synced 2024-06-01 15:41:39 +00:00
921 lines
24 KiB
C++
921 lines
24 KiB
C++
/* 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"
|
|
#include "StringBuilder.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;
|
|
}
|
|
|
|
static bool
|
|
strstarts (const char *var, const char *x)
|
|
{
|
|
return strncmp (var, x, strlen (x)) == 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 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
|
|
{
|
|
size_t line_n = 0;
|
|
char *line = NULL;
|
|
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");
|
|
|
|
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))
|
|
continue;
|
|
|
|
if (sym_text)
|
|
{
|
|
StringBuilder sb;
|
|
sb.appendf ("%s`%s", mod_name, sym_name);
|
|
char *fname = sb.toString ();
|
|
Function *func = dbeSession->createFunction ();
|
|
func->set_name (fname);
|
|
free (fname);
|
|
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;
|
|
}
|
|
}
|
|
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;
|
|
}
|