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 <sys/param.h>
23#include <sys/mman.h>
24
25#include "util.h"
26#include "DbeFile.h"
27#include "DbeSession.h"
28#include "Experiment.h"
29#include "Emsg.h"
30#include "Function.h"
31#include "LoadObject.h"
32#include "Module.h"
33#include "PRBTree.h"
34#include "Sample.h"
35#include "Elf.h"
36#include "StringBuilder.h"
37
38void
39Experiment::mrec_insert (MapRecord *mrec)
40{
41  int sz = mrecs->size ();
42  MapRecord *tmp = sz > 0 ? mrecs->fetch (sz - 1) : NULL;
43
44  // The following should work in most cases
45  if (tmp == NULL || tmp->ts <= mrec->ts)
46    {
47      mrecs->append (mrec);
48      return;
49    }
50
51  // If it didn't...
52  int lo = 0;
53  int hi = sz - 1;
54  while (lo <= hi)
55    {
56      int md = (lo + hi) / 2;
57      tmp = mrecs->fetch (md);
58      if (tmp->ts < mrec->ts)
59	lo = md + 1;
60      else
61	hi = md - 1;
62    }
63  mrecs->insert (lo, mrec);
64}
65
66int
67Experiment::process_arglist_cmd (char *, char *arglist)
68{
69  uarglist = arglist;
70
71  // find argv[0], and extract its basename
72  if (strcmp (uarglist, NTXT ("(fork)")) == 0)
73    return 0; // leaving target name NULL
74  char *p = uarglist;
75  char *pp = uarglist;
76  char *pl;
77  for (;;)
78    {
79      if (*p == '/')
80	pp = p + 1;
81      if (*p == ' ' || *p == 0)
82	{
83	  pl = p;
84	  break;
85	}
86      p++;
87    }
88  size_t len = pl - pp;
89  if (len > 0)
90    utargname = dbe_sprintf (NTXT ("%.*s"), (int) len, pp);
91  return 0;
92}
93
94int
95Experiment::process_desc_start_cmd (char *, hrtime_t ts, char *flavor,
96				    char *nexp, int follow, char *txt)
97{
98  char *str;
99  Emsg *m;
100
101  if (follow == 1)
102    str = dbe_sprintf (GTXT ("Starting %s %ld.%09ld, exp %s.er, \"%s\""),
103		       flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC),
104		       nexp, txt);
105  else
106    str = dbe_sprintf (GTXT ("Starting %s %ld.%09ld, no experiment, \"%s\""),
107		       flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC),
108		       txt);
109  m = new Emsg (CMSG_COMMENT, str);
110  free (str);
111  runlogq->append (m);
112
113  free (flavor);
114  free (nexp);
115  free (txt);
116  return 0;
117}
118
119int
120Experiment::process_desc_started_cmd (char *, hrtime_t ts, char *flavor,
121				      char *nexp, int follow, char *txt)
122{
123  char *str;
124  Emsg *m;
125
126  if (follow == 1)
127    str = dbe_sprintf (GTXT ("Started  %s %ld.%09ld, exp %s.er, \"%s\""),
128		       flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC),
129		       nexp, txt);
130  else
131    str = dbe_sprintf (GTXT ("Started  %s %ld.%09ld, no experiment, \"%s\""),
132		       flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC),
133		       txt);
134  m = new Emsg (CMSG_COMMENT, str);
135  free (str);
136  runlogq->append (m);
137  free (flavor);
138  free (nexp);
139  free (txt);
140  return 0;
141}
142
143LoadObject *
144Experiment::get_dynfunc_lo (const char *loName)
145{
146  LoadObject *lo = loadObjMap->get (loName);
147  if (lo == NULL)
148    {
149      lo = createLoadObject (loName, expIdx);// DYNFUNC_SEGMENT is always unique
150      lo->dbeFile->filetype |= DbeFile::F_FICTION;
151      lo->flags |= SEG_FLAG_DYNAMIC;
152      lo->type = LoadObject::SEG_TEXT;
153      lo->set_platform (platform, wsize);
154      append (lo);
155    }
156  return lo;
157}
158
159Function *
160Experiment::create_dynfunc (Module *mod, char *fname, int64_t vaddr,
161			    int64_t fsize)
162{
163  Function *f = dbeSession->createFunction ();
164  f->set_name (fname);
165  f->flags |= FUNC_FLAG_DYNAMIC;
166  f->size = fsize;
167  f->img_offset = vaddr;
168  f->module = mod;
169  mod->functions->append (f);
170  mod->loadobject->functions->append (f);
171  return f;
172}
173
174static int
175func_cmp (const void *a, const void *b)
176{
177  Function *fp1 = *((Function **) a);
178  Function *fp2 = *((Function **) b);
179  uint64_t i1 = fp1->img_offset;
180  uint64_t i2 = fp2->img_offset;
181  return i1 < i2 ? -1 : i1 == i2 ? 0 : 1;
182}
183
184int
185Experiment::process_fn_load_cmd (Module *mod, char *fname, Vaddr vaddr,
186				 int fsize, hrtime_t ts)
187{
188  Dprintf (DEBUG_MAPS,
189	   "process_fn_load_cmd:%s (%s) vaddr=0x%llx msize=%lld ts=%lld\n",
190	   STR (mod ? mod->get_name () : NULL), STR (fname),
191	   (unsigned long long) vaddr, (long long) fsize, (long long) ts);
192  if (mod != NULL)
193    {
194      mod->functions->sort (func_cmp);
195      uint64_t lastVaddr = vaddr;
196      for (int i = 0, sz = mod->functions->size (); i < sz; i++)
197	{
198	  Function *f = mod->functions->fetch (i);
199	  if (lastVaddr < f->img_offset)
200	    {
201	      char *fnm = dbe_sprintf (GTXT ("<static>@0x%llx (%s)"),
202				       (unsigned long long) lastVaddr, fname);
203	      create_dynfunc (mod, fnm, lastVaddr, f->img_offset - lastVaddr);
204	      free (fnm);
205	    }
206	  lastVaddr = f->img_offset + f->size;
207	}
208      if (lastVaddr < vaddr + fsize)
209	{
210	  char *fnm = dbe_sprintf (GTXT ("<static>@0x%llx (%s)"),
211				   (unsigned long long) lastVaddr, fname);
212	  create_dynfunc (mod, fnm, lastVaddr, vaddr + fsize - lastVaddr);
213	  free (fnm);
214	}
215      mod->functions->sort (func_cmp);
216      for (int i = 0, sz = mod->functions->size (); i < sz; i++)
217	{
218	  Function *f = mod->functions->fetch (i);
219	  MapRecord *mrec = new MapRecord;
220	  mrec->kind = MapRecord::LOAD;
221	  mrec->obj = f;
222	  mrec->base = f->img_offset;
223	  mrec->size = f->size;
224	  mrec->ts = ts;
225	  mrec->foff = 0;
226	  mrec_insert (mrec);
227	}
228      return 0;
229    }
230
231  LoadObject *ds = get_dynfunc_lo (DYNFUNC_SEGMENT);
232  Function *dfunc = create_dynfunc (ds->noname, fname, vaddr, fsize);
233
234  // check for special functions, USER, IDLE, TRUNC to disable offsets in disassembly
235  // XXX -- check based on name now
236  // Optimization: use pre-initialized localized strings
237  static const char * localized_USER_MODE = NULL;
238  static const char * localized_IDLE = NULL;
239  static const char * localized_TRUNCATED_STACK = NULL;
240  if (localized_USER_MODE == NULL)
241    {
242      localized_USER_MODE = GTXT ("<USER_MODE>");
243      localized_IDLE = GTXT ("<IDLE>");
244      localized_TRUNCATED_STACK = GTXT ("<TRUNCATED_STACK>");
245    }
246  if (strcmp (fname, localized_USER_MODE) == 0
247      || strcmp (fname, localized_IDLE) == 0
248      || strcmp (fname, localized_TRUNCATED_STACK) == 0)
249    dfunc->flags |= FUNC_FLAG_NO_OFFSET;
250
251  MapRecord *mrec = new MapRecord;
252  mrec->kind = MapRecord::LOAD;
253  mrec->obj = dfunc;
254  mrec->base = vaddr;
255  mrec->size = fsize;
256  mrec->ts = ts;
257  mrec->foff = 0;
258  mrec_insert (mrec);
259  return 0;
260}
261
262int
263Experiment::process_fn_unload_cmd (char *, Vaddr vaddr, hrtime_t ts)
264{
265  MapRecord *mrec = new MapRecord;
266  mrec->kind = MapRecord::UNLOAD;
267  mrec->base = vaddr;
268  mrec->ts = ts;
269  mrec_insert (mrec);
270  return 0;
271}
272
273void
274Experiment::register_metric (Metric::Type type)
275{
276  BaseMetric *mtr = dbeSession->register_metric (type);
277  metrics->append (mtr);
278}
279
280void
281Experiment::register_metric (Hwcentry *ctr, const char* aux, const char* uname)
282{
283  BaseMetric *mtr = dbeSession->register_metric (ctr, aux, uname);
284  metrics->append (mtr);
285  if (mtr->get_dependent_bm ())
286    metrics->append (mtr->get_dependent_bm ());
287}
288
289int
290Experiment::process_hwcounter_cmd (char *, int cpuver, char *counter,
291				   char * int_name, int interval, int tag,
292				   int i_tpc, char *modstr)
293{
294  char *str;
295  Emsg *m;
296  Hwcentry *ctr;
297  ABST_type tpc = (ABST_type) i_tpc;
298
299  // Use previously ignored tag to associate counter packets.
300  if (tag < 0 || tag >= MAX_HWCOUNT)
301    {
302      // invalid tag specified, warn user
303      str = dbe_sprintf (GTXT ("*** Error: HW counter tag %d out of range [%d - %d]; ignored"),
304			 tag, 0, MAX_HWCOUNT - 1);
305      m = new Emsg (CMSG_ERROR, str);
306      free (str);
307      errorq->append (m);
308      free (counter);
309      return 0;
310    }
311  if (coll_params.hw_aux_name[tag])
312    {
313      // duplicate tag used, warn user
314      str = dbe_sprintf (GTXT ("*** Error: Duplicate HW counter tag %d specified; ignored"),
315			 tag);
316      m = new Emsg (CMSG_ERROR, str);
317      free (str);
318      errorq->append (m);
319      free (counter);
320      return 0;
321    }
322  hw_cpuver = cpuver;
323
324  // map it to a machinemodel string
325  if (hw_cpuver != CPUVER_UNDEFINED)
326    {
327      free (machinemodel);
328      if (hw_cpuver == 1104)
329	machinemodel = dbe_strdup (NTXT ("t4"));
330      else if (hw_cpuver == 1110)
331	machinemodel = dbe_strdup (NTXT ("t5"));
332      else if (hw_cpuver == 1204)
333	machinemodel = dbe_strdup (NTXT ("m4"));
334      else if (hw_cpuver == 1210)
335	machinemodel = dbe_strdup (NTXT ("m5"));
336      else if (hw_cpuver == 1220)
337	machinemodel = dbe_strdup (NTXT ("m6"));
338      else if (hw_cpuver == 1230)
339	machinemodel = dbe_strdup (NTXT ("m7"));
340      else
341	machinemodel = dbe_strdup (NTXT ("generic"));
342    }
343
344  // Find the entry in the machine table, and dup it
345  ctr = new Hwcentry;
346  dbeSession->append (ctr);
347  hwc_post_lookup (ctr, counter, int_name, cpuver);
348  ctr->sort_order = tag;
349  ctr->memop = tpc;
350
351  // Check if HWC name is to be modified
352  if (modstr != NULL)
353    {
354      char *s = ctr->name;
355      ctr->name = dbe_sprintf (NTXT ("%s%s"), modstr, s);
356      s = ctr->int_name;
357      ctr->int_name = dbe_sprintf (NTXT ("%s%s"), modstr, s);
358      s = ctr->metric;
359      if (s)
360	ctr->metric = dbe_sprintf (NTXT ("%s%s"), modstr, s);
361    }
362
363  char * cname = dbe_strdup (ctr->name);
364  char * uname = dbe_strdup (hwc_i18n_metric (ctr));
365  coll_params.hw_aux_name[tag] = cname;
366  coll_params.hw_username[tag] = uname;
367  coll_params.hw_interval[tag] = interval;
368  coll_params.hw_tpc[tag] = tpc;
369  coll_params.hw_cpu_ver[tag] = cpuver;
370
371  // set hw_mode and xhw_mode?
372  coll_params.hw_mode = 1;
373  if (ABST_MEMSPACE_ENABLED (tpc))
374    {
375      // yes, dataspace data available
376      coll_params.xhw_mode = 1;
377
378      // set dataspace available
379      dataspaceavail = true;
380    }
381  register_metric (ctr, cname, uname);
382  free (counter);
383  return 0;
384}
385
386// TBR:?
387
388int
389Experiment::process_hwsimctr_cmd (char *, int cpuver, char *nm, char *int_name,
390				  char *metric, int reg,
391				  int interval, int timecvt, int i_tpc, int tag)
392{
393  char *str;
394  Emsg *m;
395  Hwcentry *ctr;
396  ABST_type tpc = (ABST_type) i_tpc;
397
398  // Use previously ignored tag to associate counter packets.
399  if (tag < 0 || tag >= MAX_HWCOUNT)
400    {
401      // invalid tag specified, warn user
402      str = dbe_sprintf (GTXT ("*** Error: HW counter tag %d out of range [%d - %d]; ignored"),
403			 tag, 0, MAX_HWCOUNT - 1);
404      m = new Emsg (CMSG_ERROR, str);
405      free (str);
406      errorq->append (m);
407
408      free (nm);
409      free (int_name);
410      free (metric);
411      return 0;
412    }
413  if (coll_params.hw_aux_name[tag])
414    {
415      // duplicate tag used, warn user
416      str = dbe_sprintf (GTXT ("*** Error: Duplicate HW counter tag %d specified; ignored"),
417			 tag);
418      m = new Emsg (CMSG_ERROR, str);
419      free (str);
420      errorq->append (m);
421      free (nm);
422      free (int_name);
423      free (metric);
424      return 0;
425    }
426  hw_cpuver = cpuver;
427  ctr = new Hwcentry;
428  {
429    static Hwcentry empty;
430    *ctr = empty;
431  }
432  ctr->name = nm;
433  ctr->int_name = int_name;
434  ctr->metric = metric;
435  ctr->reg_num = reg;
436  ctr->val = interval;
437  ctr->timecvt = timecvt;
438  ctr->memop = tpc;
439  ctr->sort_order = tag;
440
441  char *cname = dbe_strdup (ctr->name);
442  char *uname = dbe_strdup (hwc_i18n_metric (ctr));
443
444  coll_params.hw_aux_name[tag] = cname;
445  coll_params.hw_username[tag] = uname;
446  coll_params.hw_interval[tag] = interval;
447  coll_params.hw_tpc[tag] = tpc;
448  coll_params.hw_cpu_ver[tag] = cpuver;
449
450  // set hw_mode and xhw_mode?
451  coll_params.hw_mode = 1;
452  if (ABST_MEMSPACE_ENABLED (tpc))
453    {
454      coll_params.xhw_mode = 1;
455      // set dataspace available
456      if (getenv ("ANALYZER_DATASPACE_COUNT") != 0)
457	dataspaceavail = true;
458    }
459
460  register_metric (ctr, cname, uname);
461  return 0;
462}
463
464int
465Experiment::process_jcm_load_cmd (char *, Vaddr mid, Vaddr vaddr,
466				  int msize, hrtime_t ts)
467{
468  if (jmaps == NULL)
469    return 1;
470
471  JMethod *jfunc = (JMethod*) jmaps->locate_exact_match (mid, ts);
472  if (jfunc == NULL || jfunc->get_type () != Histable::FUNCTION)
473    return 1;
474
475  LoadObject *ds = get_dynfunc_lo (JAVA_COMPILED_METHODS);
476  Module *jmodule = jfunc->module;
477  Module *dmodule = ds->noname;
478  if (jmodule)
479    {
480      dmodule = dbeSession->createModule (ds, jmodule->get_name ());
481      dmodule->lang_code = Sp_lang_java;
482      dmodule->set_file_name (dbe_strdup (jmodule->file_name));
483    }
484
485  JMethod *dfunc = dbeSession->createJMethod ();
486  dfunc->flags |= FUNC_FLAG_DYNAMIC;
487  dfunc->size = msize;
488  dfunc->module = dmodule;
489  dfunc->usrfunc = jfunc;
490  dfunc->set_addr (vaddr);
491  dfunc->set_mid (mid);
492  dfunc->set_signature (jfunc->get_signature ());
493  dfunc->set_name (jfunc->get_mangled_name ());
494  ds->functions->append (dfunc);
495  dmodule->functions->append (dfunc);
496  MapRecord *mrec = new MapRecord;
497  mrec->kind = MapRecord::LOAD;
498  mrec->obj = dfunc;
499  mrec->base = vaddr;
500  mrec->size = msize;
501  mrec->ts = ts;
502  mrec->foff = 0;
503  mrec_insert (mrec);
504  return 0;
505}
506
507int
508Experiment::process_jcm_unload_cmd (char *, Vaddr /*mid*/, hrtime_t /*ts*/)
509{
510  if (jmaps == NULL)
511    return 1;
512
513  // We are ignoring this record because of the flaw in
514  // JVMPI desing that doesn't distinguish between two or more
515  // compiled instances of a method when an unload event is
516  // generated:
517  //     JVMPI_COMPILED_METHOD_LOAD( mid, addr1, ... )
518  //     JVMPI_COMPILED_METHOD_LOAD( mid, addr2, ... )
519  //     JVMPI_COMPILED_METHOD_UNLOAD( mid ) -- which one?
520  // We rely on the ability of the PRBTree algorithms to
521  // perform mapping appropriately based on timestamps.
522  return 0;
523}
524
525int
526Experiment::process_jthr_end_cmd (char *, uint64_t tid64, Vaddr jthr,
527				  Vaddr jenv, hrtime_t ts)
528{
529  int lt = 0;
530  int rt = jthreads_idx->size () - 1;
531  uint32_t ttid = mapTagValue (PROP_THRID, tid64);
532  while (lt <= rt)
533    {
534      int md = (lt + rt) / 2;
535      JThread *jthread = jthreads_idx->fetch (md);
536      if (jthread->tid < ttid)
537	lt = md + 1;
538      else if (jthread->tid > ttid)
539	rt = md - 1;
540      else
541	{
542	  for (; jthread; jthread = jthread->next)
543	    {
544	      if (jthread->jenv == jenv)
545		{
546		  jthread->end = ts;
547		  return 0;
548		}
549	    }
550	  return 0;
551	}
552    }
553  JThread *jthread = new JThread;
554  jthread->tid = mapTagValue (PROP_THRID, tid64);
555  jthread->jthr = jthr;
556  jthread->jenv = jenv;
557  jthread->jthr_id = jthreads->size ();
558  jthread->start = ZERO_TIME;
559  jthread->end = ts;
560  jthread->next = NULL;
561  jthreads->append (jthread);
562  if (lt == jthreads_idx->size ())
563    jthreads_idx->append (jthread);
564  else
565    jthreads_idx->insert (lt, jthread);
566  return 0;
567}
568
569int
570Experiment::process_jthr_start_cmd (char *, char *thread_name, char *group_name,
571				    char *parent_name, uint64_t tid64,
572				    Vaddr jthr, Vaddr jenv, hrtime_t ts)
573{
574  JThread *jthread = new JThread;
575  jthread->name = thread_name;
576  jthread->group_name = group_name;
577  jthread->parent_name = parent_name;
578  jthread->tid = mapTagValue (PROP_THRID, tid64);
579  jthread->jthr = jthr;
580  jthread->jenv = jenv;
581  jthread->jthr_id = jthreads->size ();
582  jthread->start = ts;
583  jthread->end = MAX_TIME;
584  jthread->next = NULL;
585
586  jthreads->append (jthread);
587
588  int lt = 0;
589  int rt = jthreads_idx->size () - 1;
590  while (lt <= rt)
591    {
592      int md = (lt + rt) / 2;
593      JThread *jtmp = jthreads_idx->fetch (md);
594      if (jtmp->tid < jthread->tid)
595	lt = md + 1;
596      else if (jtmp->tid > jthread->tid)
597	rt = md - 1;
598      else
599	{
600	  jthread->next = jtmp;
601	  jthreads_idx->store (md, jthread);
602	  return 0;
603	}
604    }
605  if (lt == jthreads_idx->size ())
606    jthreads_idx->append (jthread);
607  else
608    jthreads_idx->insert (lt, jthread);
609  return 0;
610}
611
612int
613Experiment::process_gc_end_cmd (
614				hrtime_t ts)
615{
616  if (gcevents->size () == 0)
617    {
618      GCEvent *gcevent = new GCEvent;
619      gcevent->start = ZERO_TIME;
620      gcevent->end = ts;
621      gcevent->id = gcevents->size () + 1;
622      gcevents->append (gcevent);
623      return 0;
624    }
625  GCEvent *gcevent = gcevents->fetch (gcevents->size () - 1);
626  if (gcevent->end == MAX_TIME)
627    gcevent->end = ts;
628  else
629    // Weird: gc_end followed by another gc_end
630    gcevent->end = ts; // extend the previous event
631  return 0;
632}
633
634int
635Experiment::process_gc_start_cmd (
636				  hrtime_t ts)
637{
638  if (gcevents->size () != 0)
639    {
640      GCEvent *gcevent = gcevents->fetch (gcevents->size () - 1);
641      // Weird: gc_start followed by another gc_start
642      if (gcevent->end == MAX_TIME)
643	return 0; // ignore nested gc_starts
644    }
645  GCEvent *gcevent = new GCEvent;
646  gcevent->start = ts;
647  gcevent->end = MAX_TIME;
648  gcevent->id = gcevents->size () + 1;
649  gcevents->append (gcevent);
650  return 0;
651}
652
653int
654Experiment::process_sample_cmd (char */*cmd*/, hrtime_t /*log_xml_time*/,
655				int sample_number, char *label)
656{
657  // sample 0 is not a sample but the starting point
658  if (sample_number == 0)
659    {
660      first_sample_label = label;
661      return 0;
662    }
663  Sample *prev_sample = samples->size () > 0 ?
664	  samples->fetch (samples->size () - 1) : NULL;
665  char *start_lable = prev_sample ?
666	  prev_sample->end_label : first_sample_label;
667  Sample *sample = new Sample (sample_number);
668  sample->start_label = dbe_strdup (start_lable);
669  sample->end_label = label;
670  samples->append (sample);
671  return 0;
672}
673
674int
675Experiment::process_sample_sig_cmd (char *, int sig)
676{
677  char *str;
678  Emsg *m;
679  str = dbe_sprintf (GTXT ("Sample signal %d"), sig);
680  m = new Emsg (CMSG_COMMENT, str);
681  free (str);
682  runlogq->append (m);
683  return 0;
684}
685
686int
687Experiment::process_seg_map_cmd (char */*cmd*/, hrtime_t ts, Vaddr vaddr,
688				 int mapsize, int /*pagesize*/, int64_t offset,
689				 int64_t modeflags, int64_t chk, char *nm)
690{
691  if (nm == NULL ||
692      strncmp (nm + 1, SP_MAP_UNRESOLVABLE, strlen (SP_MAP_UNRESOLVABLE)) == 0)
693    return 0;
694
695  LoadObject *lo = loadObjMap->get (nm);
696  if (lo == NULL)
697    {
698      if (chk == 0)
699	{
700	  char *archName = checkFileInArchive (nm, false);
701	  if (archName)
702	    {
703	      Elf *elf = new Elf (archName);
704	      if (elf->status == Elf::ELF_ERR_NONE)
705		{
706		  chk = elf->elf_checksum ();
707		}
708	      free (archName);
709	      delete elf;
710	    }
711	}
712      lo = dbeSession->find_lobj_by_name (nm, chk);
713      if (lo == NULL)
714	{
715	  // Skip non-text segments
716	  if (modeflags != (PROT_READ | PROT_EXEC))
717	    return 0;
718	  // A new segment
719	  lo = createLoadObject (nm, chk);
720	  if (strstr (nm, NTXT ("libjvm.so")))
721	    {
722	      lo->flags |= SEG_FLAG_JVM;
723	      // Make sure <JVM-System> is created
724	      (void) dbeSession->get_jvm_Function ();
725	    }
726	  else if (strstr (nm, NTXT ("libmtsk.so")))
727	    {
728	      lo->flags |= SEG_FLAG_OMP;
729	      // Make sure all pseudo functions are created
730	      for (int i = 0; i < OMP_LAST_STATE; i++)
731		(void) dbeSession->get_OMP_Function (i);
732	    }
733	  else if (dbe_strcmp (utargname, get_basename (nm)) == 0)
734	    {
735	      lo->flags |= SEG_FLAG_EXE;
736	      (void) dbeSession->comp_lobjs->get ((char *) COMP_EXE_NAME, lo);
737	    }
738	  lo->checksum = chk;
739	  //  This is the default segment type
740	  lo->type = LoadObject::SEG_TEXT;
741	  lo->flags = lo->flags | SEG_FLAG_REORDER;
742	  lo->set_platform (platform, wsize);
743	}
744      if (lo->dbeFile->get_location (false) == NULL)
745	{
746	  char *archName = checkFileInArchive (nm, false);
747	  if (archName)
748	    {
749	      lo->dbeFile->set_location (archName);
750	      lo->dbeFile->inArchive = true;
751	      lo->dbeFile->check_access (archName); // init 'sbuf'
752	      lo->dbeFile->sbuf.st_mtime = 0; // Don't check timestamps
753	      free (archName);
754	    }
755	  else
756	    {
757	      archName = checkFileInArchive (nm, true);
758	      if (archName)
759		{
760		  lo->set_archname (archName);
761		  lo->need_swap_endian = need_swap_endian;
762		}
763	    }
764	  if (!dbeSession->archive_mode)
765	    lo->sync_read_stabs ();
766	}
767      append (lo);
768    }
769  if (lo->size == 0)
770    lo->size = mapsize;
771  MapRecord *mrec = new MapRecord;
772  mrec->kind = MapRecord::LOAD;
773  mrec->obj = lo;
774  mrec->base = vaddr;
775  mrec->size = mapsize;
776  mrec->ts = ts;
777  mrec->foff = offset;
778  mrec_insert (mrec);
779  return 0;
780}
781
782int
783Experiment::process_seg_unmap_cmd (char */*cmd*/, hrtime_t ts, Vaddr vaddr)
784{
785  MapRecord *mrec = new MapRecord;
786  mrec->kind = MapRecord::UNLOAD;
787  mrec->base = vaddr;
788  mrec->ts = ts;
789  mrec_insert (mrec);
790  return 0;
791}
792
793static bool
794strstarts (const char *var, const char *x)
795{
796  return strncmp (var, x, strlen (x)) == 0;
797}
798
799int
800Experiment::process_Linux_kernel_cmd (hrtime_t ts)
801{
802  LoadObject *lo = createLoadObject ("LinuxKernel");
803  lo->flags |= SEG_FLAG_EXE;
804  lo->type = LoadObject::SEG_TEXT;
805  lo->set_platform (platform, wsize);
806  append (lo);
807  long long unsigned lo_min = (long long unsigned) (-1);
808  long long unsigned lo_max = 0;
809  Module *mod = dbeSession->createModule (lo, "LinuxKernel");
810  /*
811   * XXX need to review mod initialization
812   * A specific issue is mod->file_name.  Options include:
813   *     *) NULL
814   *            This leads to seg faults in, e.g., Timeline view.
815   *     *) "/lib/modules/$(uname -r)/kernel/kernel/ctf/ctf.ko"
816   *            This leads to garbage in the Source view.
817   *     *) "/boot/vmlinuz-$(uname -r)"
818   *            This cannot be parsed for DWARF and is sometimes not found,
819   *            but the Analyzer seems to handle such problems.
820   *     *) "LinuxKernel"
821   *            This is not a proper file name,
822   *            but again Analyzer handles the case of not finding the file or not reading DWARF from it.
823   */
824  mod->set_file_name (dbe_strdup ("LinuxKernel"));
825  char kallmodsyms_copy[MAXPATHLEN];
826  snprintf (kallmodsyms_copy, sizeof (kallmodsyms_copy), "%s/kallmodsyms",
827	    expt_name);
828  FILE *fd = fopen (kallmodsyms_copy, "r");
829  if (fd == NULL)
830    {
831      char *s = dbe_sprintf (GTXT ("*** Error: Cannot find kernel module symbols file %s; ignored"),
832			     kallmodsyms_copy);
833      Emsg *m = new Emsg (CMSG_ERROR, s);
834      free (s);
835      errorq->append (m);
836      lo_min = 0;
837    }
838  else
839    {
840      size_t line_n = 0;
841      char *line = NULL;
842      while (getline (&line, &line_n, fd) > 0)
843	{
844	  long long unsigned sym_addr;
845	  long long unsigned sym_size;
846	  char sym_type;
847	  int sym_text;
848	  char sym_name[256];
849	  char mod_name[256] = "vmlinux]"; /* note trailing ] */
850	  sscanf (line, "%llx %llx %c %s [%s", &sym_addr, &sym_size, &sym_type,
851		  sym_name, mod_name);
852	  if (line[0] == '\n' || line[0] == 0)
853	    continue;
854	  sym_text = (sym_type == 't' || sym_type == 'T');
855	  mod_name[strlen (mod_name) - 1] = '\0'; /* chop trailing ] */
856	  if (strcmp (mod_name, "ctf") == 0)
857	    strcpy (mod_name, "shared_ctf");
858
859	  if (strcmp (sym_name, "__per_cpu_start") == 0
860	      || strcmp (sym_name, "__per_cpu_end") == 0
861	      || strstarts (sym_name, "__crc_")
862	      || strstarts (sym_name, "__ksymtab_")
863	      || strstarts (sym_name, "__kcrctab_")
864	      || strstarts (sym_name, "__kstrtab_")
865	      || strstarts (sym_name, "__param_")
866	      || strstarts (sym_name, "__syscall_meta__")
867	      || strstarts (sym_name, "__p_syscall_meta__")
868	      || strstarts (sym_name, "__event_")
869	      || strstarts (sym_name, "event_")
870	      || strstarts (sym_name, "ftrace_event_")
871	      || strstarts (sym_name, "types__")
872	      || strstarts (sym_name, "args__")
873	      || strstarts (sym_name, "__tracepoint_")
874	      || strstarts (sym_name, "__tpstrtab_")
875	      || strstarts (sym_name, "__tpstrtab__")
876	      || strstarts (sym_name, "__initcall_")
877	      || strstarts (sym_name, "__setup_")
878	      || strstarts (sym_name, "__pci_fixup_")
879	      || strstarts (sym_name, "__dta_")
880	      || strstarts (sym_name, "__dtrace_probe_")
881	      || (strstr (sym_name, ".") != NULL
882		  &&  strstr (sym_name, ".clone.") == NULL))
883	    continue;
884
885	  if (sym_text)
886	    {
887	      StringBuilder sb;
888	      sb.appendf ("%s`%s", mod_name, sym_name);
889	      char *fname = sb.toString ();
890	      Function *func = dbeSession->createFunction ();
891	      func->set_name (fname);
892	      free (fname);
893	      func->size = sym_size;
894	      func->img_offset = sym_addr;
895	      func->module = mod;
896	      lo->functions->append (func);
897	      mod->functions->append (func);
898	      if (lo_min > sym_addr)
899		lo_min = sym_addr;
900	      if (lo_max < sym_addr + sym_size)
901		lo_max = sym_addr + sym_size;
902	    }
903	}
904      fclose (fd);
905      free (line);
906    }
907  lo->size = lo_max;
908  lo->functions->sort (func_cmp);
909  mod->functions->sort (func_cmp);
910
911  MapRecord *mrec = new MapRecord;
912  mrec->kind = MapRecord::LOAD;
913  mrec->obj = lo;
914  mrec->base = lo_min;
915  mrec->size = lo_max - lo_min;
916  mrec->ts = ts;
917  mrec->foff = lo_min;
918  mrec_insert (mrec);
919  return 0;
920}
921