aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Barach <dave@barachs.net>2016-02-11 19:28:19 -0500
committerGerrit Code Review <gerrit@fd.io>2016-02-12 01:30:48 +0000
commit52642c3c535128cb96489fed45327bdd55953397 (patch)
treece6e2cc9f505fbf04acbc9109adb4c204fb96c8c
parent3640d537e1c1ea7670b98f0aaf27a48c08984893 (diff)
Performance tools, initial check-in
Change-Id: I9fb4f4babecbe02d171f38c4d089634e90141937 Signed-off-by: Dave Barach <dave@barachs.net>
-rw-r--r--build-data/packages/g2.mk5
-rw-r--r--build-data/packages/perftool.mk5
-rw-r--r--g2/Makefile.am34
-rw-r--r--g2/clib.c154
-rw-r--r--g2/configure.ac13
-rw-r--r--g2/cpel.c470
-rw-r--r--g2/cpel.h83
-rw-r--r--g2/events.c475
-rw-r--r--g2/g2.h195
-rw-r--r--g2/g2version.c19
-rw-r--r--g2/main.c196
-rw-r--r--g2/menu1.c565
-rw-r--r--g2/mkversion.c77
-rw-r--r--g2/pointsel.c854
-rw-r--r--g2/props.c279
-rw-r--r--g2/props.h21
-rw-r--r--g2/view1.c3077
-rw-r--r--perftool/Makefile.am44
-rw-r--r--perftool/c2cpel.c248
-rw-r--r--perftool/configure.ac12
-rw-r--r--perftool/cpel.h83
-rw-r--r--perftool/cpel_util.c456
-rw-r--r--perftool/cpel_util.h68
-rw-r--r--perftool/cpelatency.c927
-rw-r--r--perftool/cpeldump.c638
-rw-r--r--perftool/cpelinreg.c892
-rw-r--r--perftool/cpelstate.c822
-rw-r--r--perftool/delsvec.c315
-rw-r--r--perftool/linreg.c78
-rw-r--r--perftool/new.cpelbin0 -> 1672 bytes
-rw-r--r--perftool/new.elogbin0 -> 4525 bytes
-rw-r--r--perftool/props.c280
32 files changed, 11385 insertions, 0 deletions
diff --git a/build-data/packages/g2.mk b/build-data/packages/g2.mk
new file mode 100644
index 00000000000..258eff1daa4
--- /dev/null
+++ b/build-data/packages/g2.mk
@@ -0,0 +1,5 @@
+g2_configure_depend = vppinfra-install
+
+g2_CPPFLAGS = $(call installed_includes_fn, vppinfra)
+
+g2_LDFLAGS = $(call installed_libs_fn, vppinfra)
diff --git a/build-data/packages/perftool.mk b/build-data/packages/perftool.mk
new file mode 100644
index 00000000000..300f902b08f
--- /dev/null
+++ b/build-data/packages/perftool.mk
@@ -0,0 +1,5 @@
+perftool_configure_depend = vppinfra-install
+
+perftool_CPPFLAGS = $(call installed_includes_fn, vppinfra)
+
+perftool_LDFLAGS = $(call installed_libs_fn, vppinfra)
diff --git a/g2/Makefile.am b/g2/Makefile.am
new file mode 100644
index 00000000000..8457c272fcd
--- /dev/null
+++ b/g2/Makefile.am
@@ -0,0 +1,34 @@
+# Copyright (c) 2016 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+AUTOMAKE_OPTIONS = foreign
+
+bin_PROGRAMS = g2
+
+AM_CFLAGS = -Wall
+
+g2_SOURCES = \
+ clib.c \
+ cpel.c \
+ cpel.h \
+ events.c \
+ g2.h \
+ main.c \
+ menu1.c \
+ pointsel.c \
+ props.c \
+ props.h \
+ g2version.c \
+ view1.c
+
+g2_LDADD = $(g2_LIBS) -lvppinfra -lpthread -lm
diff --git a/g2/clib.c b/g2/clib.c
new file mode 100644
index 00000000000..d0cd6195a5f
--- /dev/null
+++ b/g2/clib.c
@@ -0,0 +1,154 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2009-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <vppinfra/clib.h>
+#include <vppinfra/vec.h>
+#include <vppinfra/hash.h>
+#include <vppinfra/elog.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <time.h>
+#include "cpel.h"
+#include "g2.h"
+
+int widest_track_format;
+
+typedef struct bound_track_ {
+ u32 track;
+ u8 *track_str;
+} bound_track_t;
+
+bound_track_t *bound_tracks;
+
+uword *the_evtdef_hash; /* (event-id, event-definition) hash */
+uword *the_trackdef_hash; /* (track-id, track-definition) hash */
+
+elog_main_t elog_main;
+
+void *get_clib_event (unsigned int datum)
+{
+ elog_event_t *ep = vec_elt_at_index (elog_main.events, datum);
+ return (void *)ep;
+}
+
+/*
+ * read_clib_file
+ */
+int read_clib_file(char *clib_file)
+{
+ static FILE *ofp;
+ clib_error_t *error = 0;
+ int i;
+ elog_main_t *em = &elog_main;
+ double starttime, delta;
+
+ vec_free(em->events);
+ vec_free(em->event_types);
+ if (the_trackdef_hash)
+ hash_free(the_trackdef_hash);
+
+ the_trackdef_hash = hash_create (0, sizeof (uword));
+
+ error = elog_read_file (&elog_main, clib_file);
+
+ if (error) {
+ fformat(stderr, "%U", format_clib_error, error);
+ return (1);
+ }
+
+ if (ofp == NULL) {
+ ofp = fdopen(2, "w");
+ if (ofp == NULL) {
+ fprintf(stderr, "Couldn't fdopen(2)?\n");
+ exit(1);
+ }
+ }
+
+ em = &elog_main;
+
+ for (i = 0; i < vec_len (em->tracks); i++) {
+ u32 track_code;
+ bound_track_t * btp;
+ elog_track_t * t;
+ uword * p;
+ int track_strlen;
+
+ t = &em->tracks[i];
+ track_code = i;
+ p = hash_get(the_trackdef_hash, track_code);
+ if (p) {
+ fprintf(ofp, "track %d redefined, retain first definition\n",
+ track_code);
+ continue;
+ }
+ vec_add2(bound_tracks, btp, 1);
+ btp->track = track_code;
+ btp->track_str = t->name;
+ hash_set(the_trackdef_hash, track_code, btp - bound_tracks);
+
+ track_strlen = strlen((char *)btp->track_str);
+ if (track_strlen > widest_track_format)
+ widest_track_format = track_strlen;
+ }
+
+ initialize_events();
+
+ for (i = 0; i < vec_len (em->event_types); i++) {
+ elog_event_type_t *ep;
+ u8 *tmp;
+
+ ep = vec_elt_at_index(em->event_types, i);
+ tmp = (u8 *) vec_dup(ep->format);
+ vec_add1(tmp,0);
+ add_event_from_clib_file (ep->type_index_plus_one, (char *) tmp, i);
+ vec_free(tmp);
+ }
+
+ finalize_events();
+
+ em->events = elog_get_events (em);
+
+ cpel_event_init(vec_len(em->events));
+
+ starttime = em->events[0].time;
+
+ for (i = 0; i < vec_len (em->events); i++) {
+ elog_event_t *ep;
+
+ ep = vec_elt_at_index(em->events, i);
+
+ delta = ep->time - starttime;
+
+ add_clib_event (delta, ep->track, ep->type + 1, i);
+ }
+
+ cpel_event_finalize();
+
+ set_pid_ax_width(8*widest_track_format);
+
+ return(0);
+}
diff --git a/g2/configure.ac b/g2/configure.ac
new file mode 100644
index 00000000000..cd6522fc2ae
--- /dev/null
+++ b/g2/configure.ac
@@ -0,0 +1,13 @@
+AC_INIT(g2, 3.0)
+AM_INIT_AUTOMAKE
+
+AC_CHECK_LIB([vppinfra], [clib_mem_get_page_size],,
+ AC_MSG_ERROR([Please install the vpp-lib package]))
+AC_CHECK_HEADER([vppinfra/clib.h],,
+ AC_MSG_ERROR([Please install the vpp-dev package]))
+
+AM_PROG_AS
+AM_PROG_CC_C_O
+PKG_CHECK_MODULES(g2, gtk+-2.0)
+
+AC_OUTPUT([Makefile])
diff --git a/g2/cpel.c b/g2/cpel.c
new file mode 100644
index 00000000000..8bcc91e674e
--- /dev/null
+++ b/g2/cpel.c
@@ -0,0 +1,470 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2005-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <vppinfra/clib.h>
+#include <vppinfra/vec.h>
+#include <vppinfra/hash.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <time.h>
+#include "cpel.h"
+#include "g2.h"
+
+typedef struct bound_event_ {
+ u32 event_code;
+ u8 *event_str;
+ u8 *datum_str;
+} bound_event_t;
+
+bound_event_t *bound_events;
+
+int widest_track_format=8;
+
+typedef struct bound_track_ {
+ u32 track;
+ u8 *track_str;
+} bound_track_t;
+
+bound_track_t *bound_tracks;
+
+uword *the_strtab_hash; /* (name, base-VA) hash of all string tables */
+uword *the_evtdef_hash; /* (event-id, event-definition) hash */
+uword *the_trackdef_hash; /* (track-id, track-definition) hash */
+u8 *event_strtab; /* event string-table */
+
+void fatal(char *s)
+{
+ fprintf(stderr, "%s", s);
+ exit(1);
+}
+
+typedef enum {
+ PASS1=1,
+ PASS2=2,
+} pass_t;
+
+typedef struct {
+ int (*pass1)(cpel_section_header_t *, int, FILE *);
+ int (*pass2)(cpel_section_header_t *, int, FILE *);
+} section_processor_t;
+
+int bad_section(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ fprintf(ofp, "Bad (type 0) section, skipped...\n");
+ return(0);
+}
+
+int noop_pass(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ return(0);
+}
+
+int strtab_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ uword *p;
+ u8 *strtab_data_area = (u8 *)(sh+1);
+
+ /* Multiple string tables with the same name are Bad... */
+ p = hash_get_mem(the_strtab_hash, strtab_data_area);
+ if (p) {
+ fprintf(ofp, "Duplicate string table name %s", strtab_data_area);
+ }
+ /*
+ * Looks funny, but we really do want key = first string in the
+ * table, value = address(first string in the table)
+ */
+ hash_set_mem(the_strtab_hash, strtab_data_area, strtab_data_area);
+ if (verbose) {
+ fprintf(ofp, "String Table %s\n", strtab_data_area);
+ }
+ return(0);
+}
+
+int evtdef_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ int i, nevents;
+ event_definition_section_header_t *edh;
+ event_definition_t *ep;
+ u8 *this_strtab;
+ u32 event_code;
+ uword *p;
+ bound_event_t *bp;
+
+ edh = (event_definition_section_header_t *)(sh+1);
+ nevents = ntohl(edh->number_of_event_definitions);
+
+ if (verbose) {
+ fprintf(ofp, "Event Definition Section: %d definitions\n",
+ nevents);
+ }
+
+ p = hash_get_mem(the_strtab_hash, edh->string_table_name);
+ if (!p) {
+ fprintf(ofp, "Fatal: couldn't find string table\n");
+ return(1);
+ }
+ this_strtab = (u8 *)p[0];
+
+ initialize_events();
+
+ ep = (event_definition_t *)(edh+1);
+
+ for (i = 0; i < nevents; i++) {
+ event_code = ntohl(ep->event);
+ p = hash_get(the_evtdef_hash, event_code);
+ if (p) {
+ fprintf(ofp, "Event %d redefined, retain first definition\n",
+ event_code);
+ continue;
+ }
+ vec_add2(bound_events, bp, 1);
+ bp->event_code = event_code;
+ bp->event_str = this_strtab + ntohl(ep->event_format);
+ bp->datum_str = this_strtab + ntohl(ep->datum_format);
+ hash_set(the_evtdef_hash, event_code, bp - bound_events);
+
+ add_event_from_cpel_file(event_code, (char *) bp->event_str,
+ (char *)bp->datum_str);
+
+ ep++;
+ }
+
+ finalize_events();
+ return (0);
+}
+
+int trackdef_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ int i, nevents;
+ track_definition_section_header_t *tdh;
+ track_definition_t *tp;
+ u8 *this_strtab;
+ u32 track_code;
+ uword *p;
+ bound_track_t *btp;
+ int track_strlen;
+
+ tdh = (track_definition_section_header_t *)(sh+1);
+ nevents = ntohl(tdh->number_of_track_definitions);
+
+ if (verbose) {
+ fprintf(ofp, "Track Definition Section: %d definitions\n",
+ nevents);
+ }
+
+ p = hash_get_mem(the_strtab_hash, tdh->string_table_name);
+ if (!p) {
+ fprintf(ofp, "Fatal: couldn't find string table\n");
+ return(1);
+ }
+ this_strtab = (u8 *)p[0];
+
+ tp = (track_definition_t *)(tdh+1);
+
+ for (i = 0; i < nevents; i++) {
+ track_code = ntohl(tp->track);
+ p = hash_get(the_trackdef_hash, track_code);
+ if (p) {
+ fprintf(ofp, "track %d redefined, retain first definition\n",
+ track_code);
+ continue;
+ }
+ vec_add2(bound_tracks, btp, 1);
+ btp->track = track_code;
+ btp->track_str = this_strtab + ntohl(tp->track_format);
+ hash_set(the_trackdef_hash, track_code, btp - bound_tracks);
+
+ track_strlen = strlen((char *)btp->track_str);
+ if (track_strlen > widest_track_format)
+ widest_track_format = track_strlen;
+ tp++;
+ }
+ return (0);
+}
+
+int unsupported_pass (cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ if (verbose) {
+ fprintf(ofp, "Unsupported type %d section\n",
+ ntohl(sh->section_type));
+ }
+ return(0);
+}
+
+int event_pass2(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ event_section_header_t *eh;
+ u32 event_code, track_code, datum;
+ u64 starttime = ~0ULL;
+ int nevents;
+ int i;
+ event_entry_t *ep;
+ u64 now;
+ u64 delta;
+ u32 time0, time1;
+ double d;
+ uword *p;
+
+ eh = (event_section_header_t *)(sh+1);
+ nevents = ntohl(eh->number_of_events);
+ ticks_per_ns = ntohl(eh->clock_ticks_per_second)/1e9;
+ ep = (event_entry_t *)(eh+1);
+
+ p = hash_get_mem(the_strtab_hash, eh->string_table_name);
+ if (!p) {
+ fprintf(ofp, "Fatal: couldn't find string table\n");
+ return(1);
+ }
+ event_strtab = (u8 *)p[0];
+
+ cpel_event_init(nevents);
+
+ for (i = 0; i < nevents; i++) {
+ time0 = ntohl (ep->time[0]);
+ time1 = ntohl (ep->time[1]);
+
+ now = (((u64) time0)<<32) | time1;
+
+ /* Convert from bus ticks to usec */
+ d = now;
+ d /= ticks_per_ns;
+
+ now = d;
+
+ if (starttime == ~0ULL)
+ starttime = now;
+
+ delta = now - starttime;
+
+ /* Delta = time since first event, in usec */
+ event_code = ntohl(ep->event_code);
+ track_code = ntohl(ep->track);
+ datum = ntohl(ep->event_datum);
+
+ add_cpel_event(delta, track_code, event_code, datum);
+
+ ep++;
+ }
+ cpel_event_finalize();
+ return(0);
+}
+
+char *strtab_ref(unsigned long datum)
+{
+ return ((char *)(event_strtab + datum));
+}
+
+/*
+ * Note: If necessary, add passes / columns to this table to
+ * handle section order dependencies.
+ */
+
+section_processor_t processors[CPEL_NUM_SECTION_TYPES+1] =
+{
+ {bad_section, noop_pass}, /* type 0 -- f**ked */
+ {strtab_pass1, noop_pass}, /* type 1 -- STRTAB */
+ {unsupported_pass, noop_pass}, /* type 2 -- SYMTAB */
+ {evtdef_pass1, noop_pass}, /* type 3 -- EVTDEF */
+ {trackdef_pass1, noop_pass}, /* type 4 -- TRACKDEF */
+ {noop_pass, event_pass2}, /* type 5 -- EVENTS */
+};
+
+
+int process_section(cpel_section_header_t *sh, int verbose, FILE *ofp,
+ pass_t pass)
+{
+ u32 type;
+ type = ntohl(sh->section_type);
+ int rv;
+ int (*fp)(cpel_section_header_t *, int, FILE *);
+
+ if (type > CPEL_NUM_SECTION_TYPES) {
+ fprintf(stderr, "Unknown section type %d\n", type);
+ return(1);
+ }
+ switch(pass) {
+ case PASS1:
+ fp = processors[type].pass1;
+ break;
+
+ case PASS2:
+ fp = processors[type].pass2;
+ break;
+
+ default:
+ fprintf(stderr, "Unknown pass %d\n", pass);
+ return(1);
+ }
+
+ rv = (*fp)(sh, verbose, ofp);
+
+ return(rv);
+}
+
+int cpel_dump_file_header(cpel_file_header_t *fh, int verbose, FILE *ofp)
+{
+ time_t file_time;
+
+ if (verbose) {
+ fprintf(ofp, "CPEL file: %s-endian, version %d\n",
+ ((fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) ?
+ "little" : "big"),
+ fh->endian_version & CPEL_FILE_VERSION_MASK);
+
+ file_time = ntohl(fh->file_date);
+
+ fprintf(ofp, "File created %s", ctime(&file_time));
+ }
+
+ return(0);
+}
+
+
+int cpel_process(u8 *cpel, int verbose, FILE *ofp)
+{
+ cpel_file_header_t *fh;
+ cpel_section_header_t *sh;
+ u16 nsections;
+ u32 section_size;
+ int i;
+
+ /* First, the file header */
+ fh = (cpel_file_header_t *)cpel;
+ if (fh->endian_version != CPEL_FILE_VERSION) {
+ if (fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) {
+ fprintf(stderr, "Little endian data format not supported\n");
+ return(1);
+ }
+ fprintf(stderr, "Unsupported file version 0x%x\n",
+ fh->endian_version);
+ return(1);
+ }
+ cpel_dump_file_header(fh, verbose, ofp);
+ nsections = ntohs(fh->nsections);
+
+ /*
+ * Take two passes through the file. PASS1 builds
+ * data structures, PASS2 actually dumps the file.
+ * Just in case the sections are in an unobvious order.
+ */
+ sh = (cpel_section_header_t *)(fh+1);
+ for (i = 0; i < nsections; i++) {
+ section_size = ntohl(sh->data_length);
+
+ if(verbose) {
+ fprintf(ofp, "Section type %d, size %d\n", ntohl(sh->section_type),
+ section_size);
+ }
+
+ if(process_section(sh, verbose, ofp, PASS1))
+ return(1);
+
+ sh++;
+ sh = (cpel_section_header_t *)(((u8 *)sh)+section_size);
+ }
+
+ sh = (cpel_section_header_t *)(fh+1);
+ for (i = 0; i < nsections; i++) {
+ if(process_section(sh, verbose, ofp, PASS2))
+ return(1);
+ section_size = ntohl(sh->data_length);
+ sh++;
+ sh = (cpel_section_header_t *)(((u8 *)sh)+section_size);
+ }
+
+
+ return(0);
+}
+
+/*
+ * read_cpel_file
+ */
+int read_cpel_file(char *cpel_file)
+{
+ int verbose = 0;
+ int rv;
+ static u8 *cpel;
+ static unsigned long size;
+ static FILE *ofp;
+
+ if (cpel) {
+ unmapfile((char *)cpel, size);
+ hash_free(the_strtab_hash);
+ the_strtab_hash = 0;
+ hash_free(the_evtdef_hash);
+ the_evtdef_hash = 0;
+ hash_free(the_trackdef_hash);
+ the_trackdef_hash = 0;
+ }
+
+ cpel = (u8 *)mapfile((char *)cpel_file, &size);
+ if (cpel == 0) {
+ fprintf(stderr, "Couldn't map %s...\n", cpel_file);
+ exit(1);
+ }
+
+ if (ofp == NULL) {
+ ofp = fdopen(2, "w");
+ if (ofp == NULL) {
+ fprintf(stderr, "Couldn't fdopen(2)?\n");
+ exit(1);
+ }
+ }
+
+ the_strtab_hash = hash_create_string (0, sizeof (uword));
+ the_evtdef_hash = hash_create (0, sizeof (uword));
+ the_trackdef_hash = hash_create (0, sizeof (uword));
+
+ rv = cpel_process(cpel, verbose, ofp);
+
+ set_pid_ax_width(8*widest_track_format);
+
+ return(rv);
+}
+
+static bound_track_t generic_hex_track = {0, (u8 *) "0x%08x"};
+static bound_track_t generic_decimal_track = {0, (u8 *) "%8ld"};
+
+/*
+ * get_track_label
+ */
+char *get_track_label(unsigned long track)
+{
+ uword *p;
+ bound_track_t *tp;
+
+ p = hash_get(the_trackdef_hash, track);
+ if (p) {
+ tp = &bound_tracks[p[0]];
+ } else {
+ if (track > 65535)
+ tp = &generic_hex_track;
+ else
+ tp = &generic_decimal_track;
+ }
+ return((char *)tp->track_str);
+}
diff --git a/g2/cpel.h b/g2/cpel.h
new file mode 100644
index 00000000000..73e4aea5755
--- /dev/null
+++ b/g2/cpel.h
@@ -0,0 +1,83 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2005-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CPEL_H_
+#define _CPEL_H_ 1
+
+typedef struct cpel_file_header_ {
+ unsigned char endian_version;
+ unsigned char pad;
+ unsigned short nsections;
+ unsigned file_date;
+} cpel_file_header_t;
+
+#define CPEL_FILE_LITTLE_ENDIAN 0x80
+#define CPEL_FILE_VERSION 0x01
+#define CPEL_FILE_VERSION_MASK 0x7F
+
+typedef struct cpel_section_header_ {
+ unsigned int section_type;
+ unsigned int data_length; /* does NOT include type and itself */
+} cpel_section_header_t;
+
+#define CPEL_SECTION_STRTAB 1
+/* string at offset 0 is the name of the table */
+
+#define CPEL_SECTION_SYMTAB 2
+#define CPEL_SECTION_EVTDEF 3
+
+typedef struct event_definition_section_header_ {
+ char string_table_name[64];
+ unsigned int number_of_event_definitions;
+} event_definition_section_header_t;
+
+typedef struct event_definition_ {
+ unsigned int event;
+ unsigned int event_format;
+ unsigned int datum_format;
+} event_definition_t;
+
+#define CPEL_SECTION_TRACKDEF 4
+
+typedef struct track_definition_section_header_ {
+ char string_table_name[64];
+ unsigned int number_of_track_definitions;
+} track_definition_section_header_t;
+
+typedef struct track_definition_ {
+ unsigned int track;
+ unsigned int track_format;
+} track_definition_t;
+
+#define CPEL_SECTION_EVENT 5
+
+typedef struct event_section_header_ {
+ char string_table_name[64];
+ unsigned int number_of_events;
+ unsigned int clock_ticks_per_second;
+} event_section_header_t;
+
+typedef struct event_entry_ {
+ unsigned int time[2];
+ unsigned int track;
+ unsigned int event_code;
+ unsigned int event_datum;
+} event_entry_t;
+
+#define CPEL_NUM_SECTION_TYPES 5
+
+#endif /* _CPEL_H_ */
+
diff --git a/g2/events.c b/g2/events.c
new file mode 100644
index 00000000000..d4333bb05d1
--- /dev/null
+++ b/g2/events.c
@@ -0,0 +1,475 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2005-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <gtk/gtk.h>
+#include "g2.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+/*
+ * globals
+ */
+boolean g_little_endian;
+event_t *g_events;
+ulong g_nevents;
+pid_sort_t *g_pids;
+pid_sort_t *g_original_pids;
+int g_npids;
+pid_data_t *g_pid_data_list;
+
+/*
+ * locals
+ */
+pid_data_t **s_pidhash;
+
+/*
+ * config parameters
+ */
+
+double ticks_per_ns=1000.0;
+boolean ticks_per_ns_set;
+
+/****************************************************************************
+* event_init
+****************************************************************************/
+
+void event_init(void)
+{
+ ulong endian;
+ char *ep;
+ char *askstr;
+ int tmp;
+
+ ep = (char *)&endian;
+ endian = 0x12345678;
+ if (*ep != 0x12)
+ g_little_endian = TRUE;
+ else
+ g_little_endian = FALSE;
+
+ askstr = getprop("dont_ask_ticks_per_ns_initially");
+
+ if (askstr && (*askstr == 't' || *askstr == 'T')) {
+ tmp = atol(getprop_default("ticks_per_ns", 0));
+ if (tmp > 0) {
+ ticks_per_ns = tmp;
+ ticks_per_ns_set = TRUE;
+ }
+ }
+}
+
+/****************************************************************************
+* find_or_add_pid
+****************************************************************************/
+
+pid_data_t *find_or_add_pid (ulong pid)
+{
+ pid_data_t *pp;
+ ulong bucket;
+
+ bucket = pid % PIDHASH_NBUCKETS;
+
+ pp = s_pidhash[bucket];
+
+ if (pp == 0) {
+ pp = g_malloc0(sizeof(pid_data_t));
+ pp->pid_value = pid;
+ s_pidhash[bucket] = pp;
+ g_npids++;
+ return(pp);
+ }
+ while (pp) {
+ if (pp->pid_value == pid)
+ return(pp);
+ pp = pp->next;
+ }
+
+ pp = g_malloc0(sizeof(pid_data_t));
+ pp->pid_value = pid;
+ pp->next = s_pidhash[bucket];
+ s_pidhash[bucket] = pp;
+ g_npids++;
+ return(pp);
+}
+
+/****************************************************************************
+* pid_cmp
+****************************************************************************/
+
+int pid_cmp(const void *a1, const void *a2)
+{
+ pid_sort_t *p1 = (pid_sort_t *)a1;
+ pid_sort_t *p2 = (pid_sort_t *)a2;
+
+ if (p1->pid_value < p2->pid_value)
+ return(-1);
+ else if (p1->pid_value == p2->pid_value)
+ return(0);
+ else
+ return(1);
+}
+
+/****************************************************************************
+* make_sorted_pid_vector
+****************************************************************************/
+
+static void make_sorted_pid_vector(void)
+{
+ pid_data_t *pp;
+ pid_data_t **p_previous;
+ pid_sort_t *psp;
+ int i;
+
+ psp = g_pids = g_malloc(sizeof(pid_sort_t)*g_npids);
+
+ for (i = 0; i < PIDHASH_NBUCKETS; i++) {
+ pp = s_pidhash[i];
+ while(pp) {
+ psp->pid = pp;
+ psp->pid_value = pp->pid_value;
+ psp++;
+ pp = pp->next;
+ }
+ }
+
+ qsort(&g_pids[0], g_npids, sizeof(pid_sort_t), pid_cmp);
+
+ /* put the sort order into the pid objects */
+ psp = g_pids;
+
+ /*
+ * This is rather gross.
+ *
+ * We happen to know that whenever this function is called, the hash table
+ * structure itself is immediately torn down. So the "next" pointers in the
+ * pid_data_t elements are about to become useless.
+ *
+ * So we re-use them, to link all the pid_data_t elements together into a
+ * single unified linked list, with g_pid_data_list pointing to the head.
+ * This means we can walk all the pid_data_t objects if we really want to.
+ * Reading snapshots from disk is one example.
+ *
+ * Alternatively we could just leave the hash table in place; this is
+ * far nicer, but as it happens, trading O(n) lookups for O(1) lookups
+ * isn't actually a problem for the restricted post-tear-down usage. So for
+ * now we take the memory savings and swap our hash table for a list.
+ */
+ p_previous = &g_pid_data_list;
+ for (i = 0; i < g_npids; i++) {
+ pp = psp->pid;
+ pp->pid_index = i;
+ *p_previous = pp;
+ p_previous = &pp->next;
+ psp++;
+ }
+ *p_previous = NULL;
+
+ /*
+ * Squirrel away original (sorted) vector, so we can
+ * toggle between "chase" mode, snapshots, and the original
+ * display method on short notice
+ */
+ g_original_pids = g_malloc(sizeof(pid_sort_t)*g_npids);
+ memcpy (g_original_pids, g_pids, sizeof(pid_sort_t)*g_npids);
+}
+
+/****************************************************************************
+* read_events
+****************************************************************************/
+
+void read_events(char *filename)
+{
+ ulong *ulp;
+ ulong size;
+ event_t *ep;
+ raw_event_t *rep;
+ ulonglong start_time=0ULL;
+ ulonglong low_time;
+ boolean once=TRUE;
+ int i;
+ char tmpbuf [128];
+
+ ulp = (ulong *)mapfile(filename, &size);
+
+ if (ulp == NULL) {
+ sprintf(tmpbuf, "Couldn't open %s\n", filename);
+ infobox("Read Event Log Failure", tmpbuf);
+ return;
+ }
+
+ g_nevents = ntohl(*ulp);
+
+ if (size != (g_nevents*sizeof(raw_event_t) + sizeof(g_nevents))) {
+ sprintf(tmpbuf, "%s was damaged, or isn't an event log.\n", filename);
+ infobox("Bad Input File", tmpbuf);
+ g_nevents = 0;
+ unmapfile((char *)ulp, size);
+ return;
+ }
+
+ rep = (raw_event_t *)(ulp+1);
+
+ if (g_events)
+ g_free(g_events);
+
+ g_events = (event_t *)g_malloc(g_nevents * sizeof(event_t));
+ ep = g_events;
+
+ while (g_npids > 0) {
+ g_free((g_pids + g_npids-1)->pid);
+ g_npids--;
+ }
+ if (g_pids) {
+ g_free(g_pids);
+ g_free(g_original_pids);
+ g_pids = 0;
+ g_original_pids = 0;
+ }
+
+ s_pidhash = (pid_data_t **)g_malloc0(
+ PIDHASH_NBUCKETS*sizeof(pid_data_t *));
+
+ /* $$$ add a SEGV handler... */
+ for (i = 0; i < g_nevents; i++) {
+ if (once) {
+ once = FALSE;
+ start_time = ((ulonglong)ntohl(rep->time[0]));
+ start_time <<= 32;
+ low_time = ntohl(rep->time[1]);
+ low_time &= 0xFFFFFFFF;
+ start_time |= low_time;
+ ep->time = 0LL;
+ } else {
+ ep->time = ((ulonglong)ntohl(rep->time[0]));
+ ep->time <<= 32;
+ low_time = ntohl(rep->time[1]);
+ low_time &= 0xFFFFFFFF;
+ ep->time |= low_time;
+ ep->time -= start_time;
+ ep->time /= ticks_per_ns;
+ }
+ ep->code = ntohl(rep->code);
+ ep->pid = find_or_add_pid(ntohl(rep->pid));
+ ep->datum = ntohl(rep->datum);
+ ep->flags = 0;
+ ep++;
+ rep++;
+ }
+
+ unmapfile((char *)ulp, size);
+
+ make_sorted_pid_vector();
+ g_free(s_pidhash);
+ s_pidhash = 0;
+
+ /* Give the view-1 world a chance to reset a few things... */
+ view1_read_events_callback();
+}
+
+static event_t *add_ep;
+
+/****************************************************************************
+* cpel_event_init
+****************************************************************************/
+void cpel_event_init (ulong nevents)
+{
+ g_nevents = nevents;
+ if (g_events)
+ g_free(g_events);
+ add_ep = g_events = (event_t *)g_malloc(g_nevents * sizeof(event_t));
+ while (g_npids > 0) {
+ g_free((g_pids + g_npids-1)->pid);
+ g_npids--;
+ }
+ if (g_pids) {
+ g_free(g_pids);
+ g_free(g_original_pids);
+ g_pids = 0;
+ g_original_pids = 0;
+ }
+ s_pidhash = (pid_data_t **)g_malloc0(
+ PIDHASH_NBUCKETS*sizeof(pid_data_t *));
+}
+
+/****************************************************************************
+* add_cpel_event
+****************************************************************************/
+
+void add_cpel_event(ulonglong delta, ulong track, ulong event, ulong datum)
+{
+ event_t *ep;
+
+ ep = add_ep++;
+ ep->time = delta;
+ ep->pid = find_or_add_pid(track);
+ ep->code = event;
+ ep->datum = datum;
+ ep->flags = 0;
+}
+
+/****************************************************************************
+* add_clib_event
+****************************************************************************/
+
+void add_clib_event(double delta, unsigned short track,
+ unsigned short event, unsigned int index)
+{
+ event_t *ep;
+
+ ep = add_ep++;
+ ep->time = (ulonglong) (delta * 1e9); /* time in intger nanoseconds */
+ ep->pid = find_or_add_pid(track);
+ ep->code = event;
+ ep->datum = index;
+ ep->flags = EVENT_FLAG_CLIB;
+}
+
+/****************************************************************************
+* cpel_event_finalize
+****************************************************************************/
+
+void cpel_event_finalize(void)
+{
+ make_sorted_pid_vector();
+ g_free(s_pidhash);
+ s_pidhash = 0;
+
+ /* Give the view-1 world a chance to reset a few things... */
+ view1_read_events_callback();
+}
+
+/****************************************************************************
+* mapfile
+****************************************************************************/
+
+char *mapfile (char *file, ulong *sizep)
+{
+ struct stat statb;
+ char *rv;
+ int maphfile;
+ size_t mapfsize;
+
+ maphfile = open (file, O_RDONLY);
+
+ if (maphfile < 0)
+ return (NULL);
+
+ if (fstat (maphfile, &statb) < 0) {
+ return (NULL);
+ }
+
+ /* Don't try to mmap directories, FIFOs, semaphores, etc. */
+ if (! (statb.st_mode & S_IFREG)) {
+ return (NULL);
+ }
+
+ mapfsize = statb.st_size;
+
+ if (mapfsize < 3) {
+ close (maphfile);
+ return (NULL);
+ }
+
+ rv = mmap (0, mapfsize, PROT_READ, MAP_SHARED, maphfile, 0);
+
+ if (rv == 0) {
+ g_error ("%s mapping problem, I quit...\n", file);
+ }
+
+ close (maphfile);
+
+ if (madvise (rv, mapfsize, MADV_SEQUENTIAL) < 0) {
+ return (rv);
+ }
+
+ if (sizep) {
+ *sizep = mapfsize;
+ }
+ return (rv);
+}
+
+/****************************************************************************
+* unmapfile
+****************************************************************************/
+
+boolean unmapfile (char *addr, ulong size)
+{
+ if (munmap (addr, size) < 0) {
+ g_warning("Unmap error, addr 0x%lx size 0x%x\n",
+ (unsigned long) addr, (unsigned int)size);
+ return(FALSE);
+ }
+ return(TRUE);
+}
+
+/****************************************************************************
+* find_event_index
+* Binary search for first event whose time is >= t
+****************************************************************************/
+
+int find_event_index (ulonglong t)
+{
+ int index, bottom, top;
+ event_t *ep;
+
+ bottom = g_nevents-1;
+ top = 0;
+
+ while (1) {
+ index = (bottom + top) / 2;
+
+ ep = (g_events + index);
+
+ if (ep->time == t)
+ return(index);
+
+ if (top >= bottom) {
+ while (index > 0 && ep->time > t) {
+ ep--;
+ index--;
+ }
+ while (index < g_nevents && ep->time < t) {
+ ep++;
+ index++;
+ }
+ return(index);
+ }
+
+ if (ep->time < t)
+ top = index + 1;
+ else
+ bottom = index - 1;
+ }
+}
+
+/****************************************************************************
+* events_about
+****************************************************************************/
+
+void events_about (char *tmpbuf)
+{
+ sprintf(tmpbuf+strlen(tmpbuf), "%d total events, %.3f ticks per us\n",
+ (int)g_nevents, ticks_per_ns);
+}
diff --git a/g2/g2.h b/g2/g2.h
new file mode 100644
index 00000000000..1ab42191ca9
--- /dev/null
+++ b/g2/g2.h
@@ -0,0 +1,195 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2005-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * typedefs and so forth
+ */
+#include <sys/types.h>
+#include <gtk-2.0/gtk/gtk.h>
+#include <stdio.h>
+#include "props.h"
+
+typedef char boolean;
+typedef unsigned long long ulonglong;
+
+/*
+ * main.c
+ */
+
+GtkWidget *g_mainwindow;
+GtkWidget *g_mainvbox;
+GtkWidget *g_mainhbox;
+
+/*
+ * pointsel.c
+ */
+void point_selector_init(void);
+boolean read_event_definitions (char *filename);
+char *sxerox(char *);
+void pointsel_about(char *);
+void pointsel_next_snapshot(void);
+void initialize_events(void);
+void finalize_events(void);
+
+#define NEVENTS 100000
+
+typedef struct event_def_ {
+ ulong event;
+ char *name;
+ char *format;
+ boolean selected;
+ boolean is_clib;
+ char pad[2];
+} event_def_t;
+
+event_def_t *find_event_definition (ulong code);
+
+event_def_t g_eventdefs[NEVENTS];
+
+/*
+ * config params
+ */
+int c_maxpointsel; /* max # points shown in selector dlg */
+gint c_view1_draw_width;
+gint c_view1_draw_height;
+
+/*
+ * menu1.c
+ */
+
+void menu1_init(void);
+void modal_dialog (char *label_text, char *retry_text, char *default_value,
+ boolean (*cb)(char *));
+void infobox(char *label_text, char *text);
+/*
+ * view1.c
+ */
+GdkFont *g_font;
+GdkColor fg_black, bg_white;
+void view1_init(void);
+void view1_display(void);
+void view1_read_events_callback(void);
+void view1_display_when_idle(void);
+void view1_print_callback(GtkToggleButton *item, gpointer data);
+void view1_about(char *);
+void set_pid_ax_width(int width);
+void set_window_title(const char *filename);
+
+enum view1_tbox_fn {
+ TBOX_DRAW_BOXED = 1, /* note: order counts */
+ TBOX_DRAW_EVENT,
+ TBOX_DRAW_PLAIN,
+ TBOX_PRINT_BOXED,
+ TBOX_PRINT_EVENT,
+ TBOX_PRINT_PLAIN, /* end restriction */
+ TBOX_GETRECT_BOXED,
+ TBOX_GETRECT_EVENT,
+ TBOX_GETRECT_PLAIN,
+};
+
+enum view1_line_fn {
+ LINE_DRAW_BLACK = 1,
+ LINE_DRAW_WHITE,
+ LINE_PRINT,
+};
+
+GdkRectangle *tbox (char *s, int x, int y, enum view1_tbox_fn function);
+void line (int x1, int y1, int x2, int y2, enum view1_line_fn function);
+gint view1_handle_key_press_event (GtkWidget *widget, GdkEventKey *event);
+
+/*
+ * events.c
+ */
+
+void events_about (char *);
+
+typedef struct raw_event {
+ unsigned long time[2];
+ unsigned long pid;
+ unsigned long code;
+ unsigned long datum;
+} raw_event_t;
+
+void event_init(void);
+char *mapfile (char *file, ulong *sizep);
+boolean unmapfile (char *addr, ulong size);
+void read_events (char *);
+int find_event_index (ulonglong t);
+int read_cpel_file(char *file);
+int read_clib_file(char *file);
+void cpel_event_init(ulong);
+void add_event_from_cpel_file(ulong, char * , char *);
+void add_event_from_clib_file(unsigned int event, char *name,
+ unsigned int vec_index);
+void add_cpel_event(ulonglong delta, ulong, ulong, ulong);
+void add_clib_event(double delta, unsigned short track,
+ unsigned short event, unsigned int index);
+void cpel_event_finalize(void);
+void *get_clib_event (unsigned int datum);
+
+typedef struct pid_data {
+ struct pid_data *next;
+ ulong pid_value; /* The actual pid value */
+ ulong pid_index; /* Index in pid sort order */
+} pid_data_t;
+
+#define EVENT_FLAG_SELECT 0x00000001 /* This event is selected */
+#define EVENT_FLAG_SEARCHRSLT 0x00000002 /* This event is the search rslt */
+#define EVENT_FLAG_CLIB 0x00000004 /* clib event */
+
+typedef struct pid_sort {
+ struct pid_data *pid;
+ ulong pid_value;
+ /*
+ * This is a bit of a hack, since this is used only by the view:
+ */
+ unsigned color_index;
+} pid_sort_t;
+
+typedef struct event {
+ ulonglong time;
+ ulong code;
+ pid_data_t *pid;
+ ulong datum;
+ ulong flags;
+} event_t;
+
+
+boolean g_little_endian;
+event_t *g_events;
+ulong g_nevents;
+pid_sort_t *g_pids;
+pid_sort_t *g_original_pids;
+int g_npids;
+pid_data_t *g_pid_data_list;
+
+#define PIDHASH_NBUCKETS 20021 /* Should be prime */
+
+boolean ticks_per_ns_set;
+double ticks_per_ns;
+
+/*
+ * version.c
+ */
+const char *version_string;
+const char *minor_v_string;
+
+/*
+ * cpel.c
+ */
+char *get_track_label(unsigned long);
+int widest_track_format;
+char *strtab_ref(unsigned long);
diff --git a/g2/g2version.c b/g2/g2version.c
new file mode 100644
index 00000000000..4b6f9313fee
--- /dev/null
+++ b/g2/g2version.c
@@ -0,0 +1,19 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2005-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const char *version_string = "G2 (x86_64 GNU/Linux) major version 3.0";
+const char *minor_v_string =
+ "Built Wed Feb 3 10:58:12 EST 2016";
diff --git a/g2/main.c b/g2/main.c
new file mode 100644
index 00000000000..a782e17f2ef
--- /dev/null
+++ b/g2/main.c
@@ -0,0 +1,196 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2005-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "g2.h"
+#include "props.h"
+#include <pwd.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+/*
+ * globals
+ */
+
+GtkWidget *g_mainwindow; /* The main window */
+
+/* Graphical object heirarchy
+ *
+ * [main window]
+ * [main vbox]
+ * [main (e.g. file) menubar]
+ * [view hbox]
+ * [view bottom menu]
+ */
+
+GtkWidget *g_mainvbox;
+GtkWidget *g_mainhbox;
+
+gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ /* Allow window to be destroyed */
+ return(FALSE);
+}
+
+void destroy(GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit();
+}
+
+int main (int argc, char **argv)
+{
+ char tmpbuf [128];
+ struct passwd *pw;
+ char *event_file = 0;
+ char *cpel_file = 0;
+ char *clib_file =0;
+ char *title = "none";
+ int curarg=1;
+ char *homedir;
+
+ gtk_init(&argc, &argv);
+
+ homedir = getenv ("HOME");
+ tmpbuf[0] = 0;
+
+ if (homedir) {
+ sprintf(tmpbuf, "%s/.g2", homedir);
+ } else {
+ pw = getpwuid(geteuid());
+ if (pw) {
+ sprintf(tmpbuf, "%s/.g2", pw->pw_dir);
+ }
+ }
+ if (tmpbuf[0])
+ readprops(tmpbuf);
+
+ g_mainwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_signal_connect (GTK_OBJECT(g_mainwindow), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+
+ gtk_signal_connect (GTK_OBJECT(g_mainwindow), "destroy",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+
+ gtk_container_set_border_width(GTK_CONTAINER(g_mainwindow), 5);
+
+ g_mainvbox = gtk_vbox_new(FALSE, 0);
+ g_mainhbox = gtk_hbox_new(FALSE, 0);
+
+ /*
+ * init routines
+ */
+
+ menu1_init();
+ point_selector_init();
+ view1_init();
+ event_init();
+
+ /*
+ * Now that we're ready to rock 'n roll, see if we've been asked to
+ * press a few buttons...
+ */
+
+ while (curarg < argc) {
+ if (!strncmp(argv[curarg], "--cpel-input", 4)) {
+ curarg++;
+ if (curarg < argc) {
+ cpel_file = argv[curarg];
+ curarg++;
+ break;
+ }
+ g_error("Missing filename after --cpel-input");
+ }
+ if (!strncmp(argv[curarg], "--clib-input", 4)) {
+ curarg++;
+ if (curarg < argc) {
+ clib_file = argv[curarg];
+ curarg++;
+ break;
+ }
+ g_error("Missing filename after --cpel-input");
+ }
+
+ if (!strncmp(argv[curarg], "--pointdefs", 3)) {
+ curarg++;
+ if (curarg < argc) {
+ read_event_definitions(argv[curarg]);
+ curarg++;
+ continue;
+ }
+ g_error ("Missing filename after --pointdefs\n");
+ }
+ if (!strncmp(argv[curarg], "--event-log", 3)) {
+ curarg++;
+ if (curarg < argc) {
+ event_file = argv[curarg];
+ curarg++;
+ continue;
+ }
+ g_error ("Missing filename after --event-log\n");
+ }
+
+ if (!strncmp(argv[curarg], "--ticks-per-us", 3)) {
+ curarg++;
+ if (curarg < argc) {
+ ticks_per_ns = 0.0;
+ ticks_per_ns = atof(argv[curarg]);
+ if (ticks_per_ns == 0.0) {
+ g_error("ticks-per-ns (%s) didn't convert properly\n",
+ argv[curarg]);
+ }
+ ticks_per_ns_set = TRUE;
+ curarg++;
+ continue;
+ }
+ g_error ("Missing filename after --event-log\n");
+ }
+
+ fprintf(stderr,
+ "g2 [--pointdefs <filename>] [--event-log <filename>]\n");
+ fprintf(stderr, " [--ticks-per-us <value>]\n");
+ fprintf(stderr,
+ " [--cpel-input <filename>] [--clib-input <filename]>\n");
+ fprintf(stderr,
+ "%s\n%s\n", version_string, minor_v_string);
+ exit(0);
+ }
+
+ if (clib_file) {
+ read_clib_file (clib_file);
+ title = clib_file;
+ } else if (cpel_file) {
+ read_cpel_file(cpel_file);
+ title = cpel_file;
+ } else if (event_file) {
+ read_events(event_file);
+ title = event_file;
+ }
+
+ set_window_title(title);
+
+ gtk_signal_connect (GTK_OBJECT (g_mainwindow), "key_press_event",
+ (GtkSignalFunc) view1_handle_key_press_event, NULL);
+ gtk_container_add(GTK_CONTAINER(g_mainvbox), g_mainhbox);
+ gtk_widget_show(g_mainhbox);
+ gtk_container_add(GTK_CONTAINER(g_mainwindow), g_mainvbox);
+ gtk_widget_show(g_mainvbox);
+ gtk_widget_show(g_mainwindow);
+
+ gtk_main();
+ return(0);
+}
diff --git a/g2/menu1.c b/g2/menu1.c
new file mode 100644
index 00000000000..fce81fa66b2
--- /dev/null
+++ b/g2/menu1.c
@@ -0,0 +1,565 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2006-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <gtk/gtk.h>
+#define GTK_ENABLE_BROKEN // DGMS
+#include <gtk/gtktext.h>
+#include <stdlib.h>
+#include "g2.h"
+#include <string.h>
+
+/*
+ * locals
+ */
+static GtkWidget *s_mainmenubar;
+static GtkWidget *s_filemenu;
+static GtkWidget *s_readdefs;
+static GtkWidget *s_readevents;
+static GtkWidget *s_readeventsclock;
+static GtkWidget *s_readcpel;
+static GtkWidget *s_readclib;
+static GtkWidget *s_print;
+static GtkWidget *s_quit;
+
+static GtkWidget *s_mainfilemenu;
+static GtkWidget *s_help_general;
+static GtkWidget *s_help_about;
+static GtkWidget *s_mainhelpmenu;
+static GtkWidget *s_helpmenu;
+
+static GtkWidget *s_filesel;
+static GtkWidget *s_eventsel;
+
+typedef struct md_ {
+ GtkWidget *entry;
+ GtkWidget *label;
+ GtkWidget *dialog;
+ boolean (*callback)(char *);
+ char *retry_text;
+} md_t;
+
+char *general_help = "\n"
+"G2 is a performance event visualization tool.\n"
+"\n"
+"To view CPEL-format event data:\n"
+"g2 --cpel <filename>\n"
+"or use the File Menu->Read CPEL file option.\n"
+"\n"
+"To view vppinfra-format (.../open-repo/vppinfra/vppinfra/elog.h) event data:\n"
+"g2 --clib <filename>\n"
+"or use the File Menu->Read clib file option.\n"
+"\n"
+"To toggle event detail boxes, left-mouse-click on an event.\n"
+"\n"
+"To zoom to an area, depress the left mouse button. Move the\n"
+"mouse. Release the mouse.\n"
+"\n"
+"To use the time ruler, depress the right mouse button. Move the\n"
+"mouse. Release when done.\n"
+"\n"
+"To push a track to the bottom, <ctrl><left-mouse>\n"
+"\n"
+"To pull a track to the top, <shift><left-mouse>\n"
+"\n"
+"To selectively color/uncolor a track, <ctrl><shift><left-mouse>\n"
+"\n"
+"To make the mouse scrollwheel faster, press <shift>\n"
+"\n"
+"Hotkeys, supposedly Quake-like:\n"
+" w - zoom-in\n"
+" s - zoom-out\n"
+" a - pan-left\n"
+" d - pan-right\n"
+" r - pan-up\n"
+" f - pan-down\n"
+" t - less traces\n"
+" g - more traces\n"
+"\n"
+" e - toggle summary-mode\n"
+" c - toggle color-mode\n"
+"\n"
+" x - take snapshot\n"
+" z - go to next snapshot\n"
+" p - put snapshots to snapshots.g2 \n"
+" l - load snapshots from snapshots.g2\n"
+"\n"
+"<ctrl>q - quit\n"
+"Send comments / bug reports to the \"fd.io\" mailing list.\n";
+
+/****************************************************************************
+* debug_dialog_callback
+****************************************************************************/
+
+boolean debug_dialog_callback (char *s)
+{
+ g_print("Dialog result: %s", s);
+ return (TRUE);
+}
+
+/****************************************************************************
+* get_dialog_value
+****************************************************************************/
+
+static void get_dialog_value (GtkWidget *dialog, gpointer user_data)
+{
+ md_t *md = (md_t *)user_data;
+ char * cb_arg;
+
+ cb_arg = (char *) gtk_entry_get_text(GTK_ENTRY(md->entry));
+
+ if ((*md->callback)(cb_arg)) {
+ gtk_grab_remove(md->dialog);
+ gtk_widget_destroy(md->dialog);
+ } else {
+ gtk_label_set_text (GTK_LABEL(md->label), md->retry_text);
+ }
+}
+
+/****************************************************************************
+* modal_dialog
+****************************************************************************/
+
+void modal_dialog (char *label_text, char *retry_text, char *default_value,
+ boolean (*cb)(char *))
+{
+ GtkWidget *dialog, *label, *ok_button, *entry;
+ static md_t dlg;
+ md_t *md = &dlg;
+
+ dialog = gtk_dialog_new();
+ label = gtk_label_new(label_text);
+
+ entry = gtk_entry_new();
+ if (default_value)
+ gtk_entry_set_text(GTK_ENTRY(entry), default_value);
+
+ ok_button = gtk_button_new_with_label("OK");
+
+ md->entry = entry;
+ md->label = label;
+ md->retry_text = retry_text;
+ md->dialog = dialog;
+ if (cb)
+ md->callback = cb;
+ else
+ md->callback = debug_dialog_callback;
+
+ gtk_signal_connect (GTK_OBJECT (ok_button), "clicked",
+ GTK_SIGNAL_FUNC(get_dialog_value), (gpointer) md);
+
+ gtk_signal_connect (GTK_OBJECT (entry), "activate",
+ GTK_SIGNAL_FUNC(get_dialog_value), (gpointer) md);
+
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
+ entry);
+
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
+ ok_button);
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
+ gtk_widget_show_all(dialog);
+ gtk_widget_grab_focus(entry);
+ gtk_grab_add(dialog);
+}
+
+/****************************************************************************
+* get_eventdef_name
+****************************************************************************/
+
+static void get_eventdef_name (GtkFileSelection *sel, gpointer user_data)
+{
+ char *filename = (char *) gtk_file_selection_get_filename (
+ GTK_FILE_SELECTION(s_filesel));
+ read_event_definitions(filename);
+ set_window_title(filename);
+}
+
+/****************************************************************************
+* read_eventdef_callback
+****************************************************************************/
+
+static void read_eventdef_callback(GtkToggleButton *item, gpointer data)
+{
+
+ s_filesel = gtk_file_selection_new("Read Event Definitions From...");
+
+ gtk_file_selection_set_filename(GTK_FILE_SELECTION(s_filesel),
+ "../h/elog.h");
+
+ gtk_signal_connect (GTK_OBJECT (
+ GTK_FILE_SELECTION(s_filesel)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(get_eventdef_name), NULL);
+
+ gtk_signal_connect_object (GTK_OBJECT (
+ GTK_FILE_SELECTION(s_filesel)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ (gpointer) s_filesel);
+
+ gtk_signal_connect_object (GTK_OBJECT (
+ GTK_FILE_SELECTION(s_filesel)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ (gpointer) s_filesel);
+ gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(s_filesel));
+ gtk_widget_show (s_filesel);
+}
+
+/****************************************************************************
+* get_events_name
+****************************************************************************/
+
+static void get_events_name (GtkFileSelection *sel, gpointer user_data)
+{
+ char *filename = (char *) gtk_file_selection_get_filename (
+ GTK_FILE_SELECTION(s_eventsel));
+ read_events(filename);
+ view1_display_when_idle();
+}
+
+
+/****************************************************************************
+* get_ticks_per_ns
+****************************************************************************/
+
+static boolean get_ticks_per_ns (char *value)
+{
+ double rv;
+
+ rv = atof (value);
+
+ if (rv == 0.0 || rv > 100000)
+ return(FALSE);
+
+ ticks_per_ns = rv;
+ ticks_per_ns_set = TRUE;
+
+ gtk_widget_show(s_eventsel);
+ return(TRUE);
+}
+
+/****************************************************************************
+* read_events_callback
+****************************************************************************/
+
+static void read_events_callback(GtkToggleButton *item, gpointer data)
+{
+ char tmpbuf [32];
+
+ s_eventsel = gtk_file_selection_new("Read Events From...");
+
+ gtk_signal_connect (GTK_OBJECT (
+ GTK_FILE_SELECTION(s_eventsel)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(get_events_name), NULL);
+
+ gtk_signal_connect_object (GTK_OBJECT (
+ GTK_FILE_SELECTION(s_eventsel)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ (gpointer) s_eventsel);
+
+ gtk_signal_connect_object (GTK_OBJECT (
+ GTK_FILE_SELECTION(s_eventsel)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ (gpointer) s_eventsel);
+ gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(s_eventsel));
+
+ if (ticks_per_ns_set)
+ gtk_widget_show (s_eventsel);
+ else {
+ sprintf(tmpbuf, "%.3f", ticks_per_ns);
+ modal_dialog ("Please enter clock ticks per nanosecond",
+ "Invalid: Please enter clock ticks per nanosecond",
+ tmpbuf, get_ticks_per_ns);
+ }
+}
+
+/****************************************************************************
+* read_eventclock_callback
+****************************************************************************/
+
+static void read_eventsclock_callback(GtkToggleButton *item, gpointer data)
+{
+ ticks_per_ns_set = FALSE;
+ read_events_callback(item, data);
+}
+
+/****************************************************************************
+* infobox_size_request
+****************************************************************************/
+
+void infobox_size_request (GtkWidget *widget, GtkRequisition *req,
+ gpointer user_data)
+{
+ char *text = (char *)user_data;
+ char *cp;
+ int widest_line_in_chars;
+ int w;
+ int nlines;
+
+ /*
+ * You'd think that the string extent function would work here.
+ * You'd be wrong.
+ */
+ nlines = w = widest_line_in_chars = 0;
+ for (cp = text; *cp; cp++) {
+ if (*cp == '\n') {
+ if (w > widest_line_in_chars) {
+ widest_line_in_chars = w;
+ }
+ w = 0;
+ nlines++;
+ }
+ w++;
+ }
+
+ nlines++;
+
+ req->width = (widest_line_in_chars * 8) + 20;
+ req->height = (nlines * 13) + 10;
+}
+
+/****************************************************************************
+* infobox
+****************************************************************************/
+
+void infobox(char *label_text, char *text)
+{
+ GtkWidget *dialog, *label, *ok_button, *entry;
+ GtkWidget *box;
+
+ dialog = gtk_dialog_new();
+ label = gtk_label_new(label_text);
+
+ entry = gtk_text_new(NULL, NULL);
+
+ gtk_signal_connect (GTK_OBJECT (entry), "size-request",
+ GTK_SIGNAL_FUNC(infobox_size_request),
+ (gpointer) text);
+
+ gtk_text_insert(GTK_TEXT(entry), g_font, &fg_black, &bg_white,
+ text, -1);
+
+ gtk_text_set_editable(GTK_TEXT(entry), FALSE);
+
+ ok_button = gtk_button_new_with_label("OK");
+
+ gtk_signal_connect_object (GTK_OBJECT (ok_button), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ (gpointer) GTK_OBJECT(dialog));
+
+ box = gtk_vbox_new(FALSE, 5);
+
+
+ gtk_box_pack_start(GTK_BOX(box), entry, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(box), ok_button, FALSE, FALSE, 0);
+
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
+ box);
+
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
+ gtk_widget_show_all(dialog);
+}
+
+/****************************************************************************
+* help_general_callback
+****************************************************************************/
+
+static void help_general_callback(GtkToggleButton *item, gpointer data)
+{
+ infobox("General Help", general_help);
+}
+
+/****************************************************************************
+* help_about_callback
+****************************************************************************/
+
+static void help_about_callback(GtkToggleButton *item, gpointer data)
+{
+ char tmpbuf [1024];
+ sprintf (tmpbuf, "G2 -- Graphical Event Viewer\n\n");
+ view1_about(tmpbuf);
+ pointsel_about(tmpbuf);
+ events_about(tmpbuf);
+ sprintf (tmpbuf+strlen(tmpbuf), "\n%s\n", version_string);
+ sprintf (tmpbuf+strlen(tmpbuf), "%s\n", minor_v_string);
+ infobox("About", tmpbuf);
+}
+
+
+/****************************************************************************
+* get_cpel_name
+****************************************************************************/
+
+static void get_cpel_name (GtkFileSelection *sel, gpointer user_data)
+{
+ char *filename = (char *)gtk_file_selection_get_filename (
+ GTK_FILE_SELECTION(s_filesel));
+ read_cpel_file(filename);
+ set_window_title(filename);
+}
+
+/****************************************************************************
+* get_clib_name
+****************************************************************************/
+
+static void get_clib_name (GtkFileSelection *sel, gpointer user_data)
+{
+ char *filename = (char *) gtk_file_selection_get_filename (
+ GTK_FILE_SELECTION(s_filesel));
+ read_clib_file(filename);
+ set_window_title(filename);
+}
+
+/****************************************************************************
+* read_cpel_callback
+****************************************************************************/
+
+static void read_cpel_callback(GtkToggleButton *item, gpointer data)
+{
+
+ s_filesel = gtk_file_selection_new("Read CPEL data from...");
+
+ gtk_file_selection_set_filename(GTK_FILE_SELECTION(s_filesel),
+ "cpel.out");
+
+ gtk_signal_connect (GTK_OBJECT (
+ GTK_FILE_SELECTION(s_filesel)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(get_cpel_name), NULL);
+
+ gtk_signal_connect_object (GTK_OBJECT (
+ GTK_FILE_SELECTION(s_filesel)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ (gpointer) s_filesel);
+
+ gtk_signal_connect_object (GTK_OBJECT (
+ GTK_FILE_SELECTION(s_filesel)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ (gpointer) s_filesel);
+ gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(s_filesel));
+ gtk_widget_show (s_filesel);
+}
+
+/****************************************************************************
+* read_clib_callback
+****************************************************************************/
+
+static void read_clib_callback(GtkToggleButton *item, gpointer data)
+{
+
+ s_filesel = gtk_file_selection_new("Read clib data From...");
+
+ gtk_file_selection_set_filename(GTK_FILE_SELECTION(s_filesel),
+ "clib.out");
+
+ gtk_signal_connect (GTK_OBJECT (
+ GTK_FILE_SELECTION(s_filesel)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC(get_clib_name), NULL);
+
+ gtk_signal_connect_object (GTK_OBJECT (
+ GTK_FILE_SELECTION(s_filesel)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ (gpointer) s_filesel);
+
+ gtk_signal_connect_object (GTK_OBJECT (
+ GTK_FILE_SELECTION(s_filesel)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ (gpointer) s_filesel);
+ gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(s_filesel));
+ gtk_widget_show (s_filesel);
+}
+
+/****************************************************************************
+* menu1_init
+****************************************************************************/
+
+void menu1_init(void)
+{
+
+ s_filemenu = gtk_menu_new();
+
+ s_readcpel = gtk_menu_item_new_with_label
+ ("Read CPEL file");
+ gtk_menu_append(GTK_MENU(s_filemenu), s_readcpel);
+ gtk_signal_connect(GTK_OBJECT(s_readcpel), "activate",
+ GTK_SIGNAL_FUNC(read_cpel_callback), 0);
+
+ s_readclib = gtk_menu_item_new_with_label
+ ("Read CLIB file");
+ gtk_menu_append(GTK_MENU(s_filemenu), s_readclib);
+ gtk_signal_connect(GTK_OBJECT(s_readclib), "activate",
+ GTK_SIGNAL_FUNC(read_clib_callback), 0);
+
+ s_readdefs = gtk_menu_item_new_with_label ("Read Event Definitions");
+ gtk_menu_append(GTK_MENU(s_filemenu), s_readdefs);
+ gtk_signal_connect(GTK_OBJECT(s_readdefs), "activate",
+ GTK_SIGNAL_FUNC(read_eventdef_callback), 0);
+
+ s_readevents = gtk_menu_item_new_with_label ("Read Event Log");
+ gtk_menu_append(GTK_MENU(s_filemenu), s_readevents);
+ gtk_signal_connect(GTK_OBJECT(s_readevents), "activate",
+ GTK_SIGNAL_FUNC(read_events_callback), 0);
+
+ s_readeventsclock = gtk_menu_item_new_with_label
+ ("Read Event Log with Different Clock Rate");
+ gtk_menu_append(GTK_MENU(s_filemenu), s_readeventsclock);
+ gtk_signal_connect(GTK_OBJECT(s_readeventsclock), "activate",
+ GTK_SIGNAL_FUNC(read_eventsclock_callback), 0);
+
+ s_print = gtk_menu_item_new_with_label ("Print");
+ gtk_menu_append(GTK_MENU(s_filemenu), s_print);
+ gtk_signal_connect(GTK_OBJECT(s_print), "activate",
+ GTK_SIGNAL_FUNC(view1_print_callback), 0);
+
+ s_quit = gtk_menu_item_new_with_label ("Exit");
+ gtk_menu_append(GTK_MENU(s_filemenu), s_quit);
+ gtk_signal_connect(GTK_OBJECT(s_quit), "activate",
+ GTK_SIGNAL_FUNC(gtk_main_quit), 0);
+
+ s_mainfilemenu = gtk_menu_item_new_with_label("File");
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(s_mainfilemenu), s_filemenu);
+
+ s_helpmenu = gtk_menu_new();
+
+ s_help_general = gtk_menu_item_new_with_label ("General");
+ gtk_menu_append(GTK_MENU(s_helpmenu), s_help_general);
+ gtk_signal_connect(GTK_OBJECT(s_help_general), "activate",
+ GTK_SIGNAL_FUNC(help_general_callback), 0);
+
+ s_help_about = gtk_menu_item_new_with_label ("About");
+ gtk_menu_append(GTK_MENU(s_helpmenu), s_help_about);
+ gtk_signal_connect(GTK_OBJECT(s_help_about), "activate",
+ GTK_SIGNAL_FUNC(help_about_callback), 0);
+
+ s_mainhelpmenu = gtk_menu_item_new_with_label("Help");
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(s_mainhelpmenu), s_helpmenu);
+
+ s_mainmenubar = gtk_menu_bar_new();
+ gtk_menu_bar_append(GTK_MENU_BAR(s_mainmenubar), s_mainfilemenu);
+ gtk_menu_bar_append(GTK_MENU_BAR(s_mainmenubar), s_mainhelpmenu);
+ gtk_widget_show_all(s_mainmenubar);
+
+ gtk_box_pack_start(GTK_BOX(g_mainvbox), s_mainmenubar, FALSE, FALSE, 0);
+}
diff --git a/g2/mkversion.c b/g2/mkversion.c
new file mode 100644
index 00000000000..3523fbe6c6d
--- /dev/null
+++ b/g2/mkversion.c
@@ -0,0 +1,77 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 1997-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+
+int main (int argc, char **argv)
+{
+ time_t now;
+ FILE *ofp;
+ char *dateval;
+ char *username;
+ char *userstr;
+ char *datestr;
+ int i;
+ char propname[32];
+ char *propvalue;
+ char timestr[64];
+ char *cp;
+
+ if (argc < 4) {
+ printf ("usage: mkversion ostype version outputfile\n");
+ exit (1);
+ }
+
+ ofp = fopen (argv[3], "w");
+ if (ofp == NULL) {
+ printf ("Couldn't create %s\n", argv[3]);
+ exit (1);
+ }
+
+ now = time (0);
+
+ fprintf (ofp, "/*\n");
+ fprintf (ofp, " * G2 Version Stamp, %s",
+ ctime (&now));
+ fprintf (ofp, " * Automatically generated, hand edits are pointless.\n");
+ fprintf (ofp, " */\n\n");
+
+ fprintf (ofp,
+ "const char *version_string = \"G2 (%s) major version %s\";\n",
+ argv[1], argv[2]);
+
+ username = (char *) cuserid (0);
+
+ strcpy(timestr, ctime(&now));
+
+ cp = timestr;
+
+ while (*cp) {
+ cp++;
+ }
+ if (*--cp == '\n')
+ *cp = 0;
+
+ fprintf (ofp,
+ "const char *minor_v_string = \"Built by %s at %s\";\n",
+ username, timestr);
+
+ exit (0);
+}
+
+
diff --git a/g2/pointsel.c b/g2/pointsel.c
new file mode 100644
index 00000000000..018dc2139e6
--- /dev/null
+++ b/g2/pointsel.c
@@ -0,0 +1,854 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2005-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <ctype.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include "g2.h"
+
+/*
+ * globals
+ */
+event_def_t g_eventdefs[NEVENTS];
+
+/*
+ * locals
+ */
+static GtkWidget *s_pointselbox;
+static FILE *s_hfp;
+static FILE *s_elog_hfp;
+static int s_basenum;
+static GtkWidget *s_event_buttons[NEVENTS];
+static int s_min_shown_pointsel;
+static int s_max_shown_pointsel;
+static GtkWidget *s_allbutton;
+static GtkWidget *s_nonebutton;
+static GtkWidget *s_pointselbuttons;
+static GtkWidget *s_ps_vscroll;
+static GtkObject *s_ps_vsadj;
+static int g_neventdefs;
+
+enum button_click {
+ ALL_BUTTON=1,
+ NONE_BUTTON,
+};
+
+/*
+ * config params
+ */
+int c_maxpointsel;
+
+/****************************************************************************
+* recompute_vscrollbar
+****************************************************************************/
+
+static void recompute_ps_vscrollbar (void)
+{
+ GtkAdjustment *adj;
+ ulong limit;
+
+ adj = GTK_ADJUSTMENT(s_ps_vsadj);
+
+#ifdef NOTDEF
+ /* This seems like the right calculation, but seems not to work */
+ if (g_neventdefs > c_maxpointsel)
+ limit = g_neventdefs - c_maxpointsel;
+ else
+ limit = g_neventdefs;
+#else
+ limit = g_neventdefs-1;
+#endif
+
+ adj->lower = (gfloat)0.00;
+ adj->upper = (gfloat)limit;
+ adj->value = (gfloat)0.00;
+ adj->step_increment = (gfloat)1.00;
+ adj->page_increment = (gfloat)(c_maxpointsel / 3);
+ adj->page_size = (gfloat)c_maxpointsel;
+ gtk_adjustment_changed(adj);
+ gtk_adjustment_value_changed(adj);
+ gtk_widget_show(s_ps_vscroll);
+}
+
+/****************************************************************************
+* point_select_callback
+****************************************************************************/
+
+static void point_select_callback(GtkToggleButton *item, gpointer data)
+{
+ int i = (int) (unsigned long long) data;
+
+ g_eventdefs[i].selected = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(s_event_buttons[i]));
+ view1_display_when_idle();
+}
+
+/****************************************************************************
+* up_button
+****************************************************************************/
+
+static void up_button(void)
+{
+ int i;
+ int increment = c_maxpointsel/4;
+
+ if (s_min_shown_pointsel == 0)
+ return;
+
+ s_min_shown_pointsel -= increment;
+
+ if (s_min_shown_pointsel < 0)
+ s_min_shown_pointsel = 0;
+
+ s_max_shown_pointsel = s_min_shown_pointsel + c_maxpointsel;
+
+ for (i = 0; i < g_neventdefs; i++) {
+ if (i >= s_min_shown_pointsel &&
+ i <= s_max_shown_pointsel)
+ gtk_widget_show(s_event_buttons[i]);
+ else
+ gtk_widget_hide(s_event_buttons[i]);
+ }
+
+}
+
+#ifdef NOTDEF
+/****************************************************************************
+* down_button
+****************************************************************************/
+
+static void down_button(void)
+{
+ int i;
+ int increment = c_maxpointsel/4;
+
+ if (s_max_shown_pointsel == g_neventdefs)
+ return;
+
+ s_max_shown_pointsel += increment;
+
+ if (s_max_shown_pointsel >= g_neventdefs)
+ s_max_shown_pointsel = (g_neventdefs-1);
+
+ s_min_shown_pointsel = s_max_shown_pointsel - c_maxpointsel;
+
+ if (s_min_shown_pointsel < 0)
+ s_min_shown_pointsel = 0;
+
+ for (i = 0; i < g_neventdefs; i++) {
+ if (i >= s_min_shown_pointsel &&
+ i <= s_max_shown_pointsel)
+ gtk_widget_show(s_event_buttons[i]);
+ else
+ gtk_widget_hide(s_event_buttons[i]);
+ }
+
+}
+#endif
+
+/****************************************************************************
+* button_click_callback
+****************************************************************************/
+
+static void button_click_callback(GtkButton *item, gpointer data)
+{
+ int i;
+ enum button_click click = (enum button_click)data;
+
+ switch (click) {
+ case ALL_BUTTON:
+ for (i = 0; i < g_neventdefs; i++) {
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON(s_event_buttons[i]), TRUE);
+ g_eventdefs[i].selected = TRUE;
+ }
+ break;
+
+ case NONE_BUTTON:
+ for (i = 0; i < g_neventdefs; i++) {
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON(s_event_buttons[i]), FALSE);
+ g_eventdefs[i].selected = FALSE;
+ }
+ break;
+ }
+}
+
+/****************************************************************************
+* scroll_callback
+****************************************************************************/
+
+static void scroll_callback (GtkAdjustment *adj, GtkWidget *notused)
+{
+ int i;
+
+ s_min_shown_pointsel = (int)adj->value;
+ s_max_shown_pointsel = s_min_shown_pointsel + c_maxpointsel;
+
+ for (i = 0; i < g_neventdefs; i++) {
+ if (i >= s_min_shown_pointsel &&
+ i <= s_max_shown_pointsel)
+ gtk_widget_show(s_event_buttons[i]);
+ else
+ gtk_widget_hide(s_event_buttons[i]);
+ }
+}
+
+/****************************************************************************
+* point_selector_init
+****************************************************************************/
+
+void point_selector_init(void)
+{
+
+ c_maxpointsel = atol(getprop_default("event_selector_lines", "20"));
+
+ s_pointselbox = gtk_vbox_new(FALSE,5);
+
+ s_pointselbuttons = gtk_hbox_new(FALSE,5);
+
+ s_allbutton = gtk_button_new_with_label("ALL");
+ gtk_widget_show(s_allbutton);
+ s_nonebutton = gtk_button_new_with_label("NONE");
+ gtk_widget_show(s_nonebutton);
+
+ gtk_signal_connect (GTK_OBJECT(s_allbutton), "clicked",
+ GTK_SIGNAL_FUNC(button_click_callback),
+ (gpointer) ALL_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_nonebutton), "clicked",
+ GTK_SIGNAL_FUNC(button_click_callback),
+ (gpointer) NONE_BUTTON);
+
+ gtk_box_pack_start(GTK_BOX(s_pointselbuttons), s_allbutton, FALSE,
+ FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(s_pointselbuttons), s_nonebutton, FALSE,
+ FALSE, 0);
+
+ gtk_widget_show(s_pointselbuttons);
+ gtk_widget_ref(s_pointselbuttons);
+
+ gtk_box_pack_start(GTK_BOX(s_pointselbox), s_pointselbuttons, FALSE,
+ FALSE, 0);
+
+ gtk_box_pack_end (GTK_BOX(g_mainhbox), s_pointselbox,
+ FALSE, FALSE, 0);
+
+ s_ps_vsadj = gtk_adjustment_new(0.0 /* initial value */,
+ 0.0 /* minimum value */,
+ 2000.0 /* maximum value */,
+ 0.1 /* step increment */,
+ 10.0/* page increment */,
+ 10.0/* page size */);
+
+ s_ps_vscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT(s_ps_vsadj));
+ gtk_signal_connect (GTK_OBJECT (s_ps_vsadj), "value-changed",
+ GTK_SIGNAL_FUNC (scroll_callback),
+ (gpointer)s_ps_vscroll);
+ gtk_box_pack_end(GTK_BOX(g_mainhbox), s_ps_vscroll, FALSE, FALSE, 0);
+}
+
+/****************************************************************************
+* sxerox
+****************************************************************************/
+
+char *sxerox (char *s)
+{
+ char *rv;
+
+ /* Note: g_malloc does or dies... */
+ rv = (char *)g_malloc(strlen(s)+1);
+ strcpy (rv, s);
+ return (rv);
+}
+
+/****************************************************************************
+* reset_point_selector
+****************************************************************************/
+
+static void reset_point_selector(void)
+{
+ int i;
+
+ gtk_widget_hide(s_pointselbox);
+ gtk_widget_hide(s_pointselbuttons);
+ gtk_widget_hide(s_ps_vscroll);
+ gtk_container_remove(GTK_CONTAINER(s_pointselbox),
+ s_pointselbuttons);
+
+ for (i = 0; i < g_neventdefs; i++) {
+ if (s_event_buttons[i]) {
+ gtk_container_remove(GTK_CONTAINER(s_pointselbox),
+ s_event_buttons[i]);
+ s_event_buttons[i] = 0;
+ }
+ }
+}
+
+/****************************************************************************
+* create_point_selector
+****************************************************************************/
+
+static void create_point_selector(void)
+{
+ int i;
+ char tmpbuf [1024];
+ event_def_t *ep;
+ GtkWidget *wp;
+
+ for (i = 0; i < g_neventdefs; i++) {
+ ep = &g_eventdefs[i];
+ sprintf(tmpbuf, "[%lu] %s", ep->event,
+ ep->name ? ep->name : "(none)");
+ /* Hack to reduce width of point selectors */
+ if (strlen(tmpbuf) > 50) {
+ tmpbuf[50] = 0;
+ }
+
+ wp = gtk_check_button_new_with_label (tmpbuf);
+ s_event_buttons[i] = wp;
+ gtk_signal_connect (GTK_OBJECT(wp), "toggled",
+ GTK_SIGNAL_FUNC(point_select_callback),
+ (gpointer) (unsigned long long) i);
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON(wp), TRUE);
+ gtk_box_pack_start(GTK_BOX(s_pointselbox), wp, FALSE, FALSE, 0);
+ }
+
+ /* set up scroll parameters by faking an up-button */
+ s_min_shown_pointsel = 1;
+ up_button();
+
+ gtk_box_pack_start(GTK_BOX(s_pointselbox), s_pointselbuttons, FALSE,
+ FALSE, 0);
+ gtk_widget_show(s_pointselbuttons);
+ gtk_widget_show(s_pointselbox);
+ gtk_widget_show(s_ps_vscroll);
+}
+
+/****************************************************************************
+* remove_all_events
+****************************************************************************/
+
+static void remove_all_events(void)
+{
+ event_def_t *ep;
+ int i;
+
+ for (i = 0; i < g_neventdefs; i++) {
+ ep = &g_eventdefs[i];
+ if (!ep->is_clib) {
+ if (ep->name)
+ g_free(ep->name);
+ if(ep->format)
+ g_free(ep->format);
+ }
+ }
+ g_neventdefs = 0;
+}
+
+/****************************************************************************
+* add_event
+****************************************************************************/
+
+static void add_event(ulong event, char *name, char *format)
+{
+ int i;
+ event_def_t *ep;
+
+ if (g_neventdefs >= NEVENTS) {
+ g_error("Too many event definitions, increase NEVENTS!");
+ /*NOTREACHED*/
+ }
+
+ /* Simple dup check, probably not needed very often */
+ for (i = 0; i < g_neventdefs; i++) {
+ if (g_eventdefs[i].event == event) {
+ g_warning("Duplicate def event %lu: first definition retained\n",
+ event);
+ return;
+ }
+ }
+
+ ep = &g_eventdefs[g_neventdefs++];
+
+ ep->event = event;
+ ep->name = sxerox(name);
+ ep->format = sxerox(format);
+ ep->selected = TRUE;
+}
+
+/****************************************************************************
+* add_event_from_cpel_file
+****************************************************************************/
+
+void add_event_from_cpel_file(ulong event, char *event_format,
+ char *datum_format)
+{
+ event_def_t *ep;
+
+ if (g_neventdefs >= NEVENTS) {
+ g_error("Too many event definitions, increase NEVENTS!");
+ /*NOTREACHED*/
+ }
+
+ ep = &g_eventdefs[g_neventdefs++];
+
+ ep->event = event;
+ /*
+ * Duplicate the strings for backward compatibility. Otherwise,
+ * the g_free above will barf because the name/format strings are
+ * actually in mmap'ed memory
+ */
+ ep->name = sxerox(event_format);
+ ep->format = sxerox(datum_format);
+ ep->selected = TRUE;
+}
+
+/****************************************************************************
+* add_event_from_clib_file
+****************************************************************************/
+
+void add_event_from_clib_file(unsigned int event, char *name,
+ unsigned int vec_index)
+{
+ event_def_t *ep;
+
+ if (g_neventdefs >= NEVENTS) {
+ g_error("Too many event definitions, increase NEVENTS!");
+ /*NOTREACHED*/
+ }
+
+ ep = &g_eventdefs[g_neventdefs++];
+
+ ep->event = event;
+
+ ep->name = sxerox(name);
+ ep->format = (void *)(unsigned long long) vec_index;
+ ep->selected = TRUE;
+ ep->is_clib = TRUE;
+}
+
+/****************************************************************************
+* read_header_file - eats header file lines of the form
+*
+* #define EVENT_FOO 123 / * name: %d * /
+*
+****************************************************************************/
+
+static void read_header_file (void)
+{
+ char tmpbuf [1024];
+ char *name, *format;
+ char *cp;
+ unsigned long event;
+ int ev_num_flag;
+
+ while (fgets (tmpbuf, sizeof (tmpbuf), s_hfp))
+ {
+ cp = tmpbuf;
+ ev_num_flag = 0;
+
+ if (strncmp (cp, "#define", 7))
+ continue;
+
+ /* skip #define */
+ while (*cp && !(isspace ((int)*cp)))
+ cp++;
+
+ if (*cp == 0)
+ continue;
+
+ /* skip ws after #define */
+ while (*cp && isspace ((int)*cp))
+ cp++;
+
+ if (*cp == 0)
+ continue;
+
+ /* skip symbolic name */
+ while (*cp && !(isspace ((int)*cp)))
+ cp++;
+
+ if (*cp == 0)
+ continue;
+
+ /* skip ws after symbolic name */
+ while (*cp && isspace ((int)*cp))
+ cp++;
+
+ if (*cp == 0)
+ continue;
+
+ event = 0;
+
+ if (!strncmp(cp, "EV_NUM", 6)) {
+ cp += 6;
+ ev_num_flag = 1;
+
+ while (*cp && *cp != '(')
+ cp++;
+
+ if (*cp == 0)
+ continue;
+
+ cp++;
+
+ while (*cp && isspace ((int)*cp))
+ cp++;
+
+ }
+
+ /* eat event code. */
+ while (*cp && isdigit ((int)*cp))
+ {
+ event = event * 10 + (*cp - '0');
+ cp++;
+ }
+
+ if (*cp == 0)
+ continue;
+
+ if (ev_num_flag) {
+ while (*cp && *cp != ')')
+ cp++;
+ if (*cp == 0)
+ continue;
+ cp++;
+ event += s_basenum;
+ }
+
+ /* skip ws after event code */
+ while (*cp && isspace ((int)*cp))
+ cp++;
+
+ if (*cp != '/')
+ continue;
+
+ cp++;
+
+ if (*cp != '*')
+ continue;
+
+ cp++;
+
+ /* skip ws after comment start */
+ while (*cp && isspace ((int)*cp))
+ cp++;
+
+ if (*cp == 0)
+ continue;
+
+ name = cp;
+
+ /* accumulate name */
+ while (*cp && *cp != ':' && *cp != '*')
+ cp++;
+
+ if (*cp == 0)
+ continue;
+
+ *cp++ = 0;
+
+ /* skip ws after name: */
+ while (*cp && isspace ((int)*cp))
+ cp++;
+
+ if (*cp == 0 || *cp == '/')
+ {
+ format = " ";
+ goto write_it;
+ }
+
+ format = cp;
+
+ /* accumulate format string */
+ while (*cp && !isspace ((int)*cp))
+ cp++;
+
+ *cp = 0;
+
+ write_it:
+
+ add_event (event, name, format);
+ }
+}
+
+/****************************************************************************
+* read_header_files - eats header file lines of the form
+*
+* #define FILE1_BASE 100 / * pointdefs: ../vpn/vpn_points.h * /
+*
+****************************************************************************/
+
+static boolean read_header_files (void)
+{
+ char *cp, *name;
+ char tmpbuf [1024];
+ int basenum;
+ boolean rv=FALSE;
+
+ while (fgets (tmpbuf, sizeof (tmpbuf), s_elog_hfp))
+ {
+ cp = tmpbuf;
+
+ if (strncmp (cp, "#define", 7))
+ continue;
+
+ cp += 7;
+
+ /* skip ws after #define */
+ while (*cp && isspace ((int)*cp))
+ cp++;
+
+ if (*cp == 0)
+ continue;
+
+ /* skip EV_COMPxxx_START */
+ while (*cp && !isspace((int)*cp))
+ cp++;
+
+ if (*cp == 0)
+ continue;
+
+ /* skip ws after EV_COMPxxx_START */
+ while (*cp && isspace ((int)*cp))
+ cp++;
+
+ if (*cp == 0)
+ continue;
+
+ basenum = atol (cp);
+
+ /* skip #define */
+ while (*cp && (*cp != '/'))
+ cp++;
+
+ if (*cp == 0)
+ continue;
+
+ cp++;
+ if (*cp != '*')
+ continue;
+
+ cp++;
+
+ /* skip ws after comment start */
+ while (*cp && isspace ((int)*cp))
+ cp++;
+
+ if (*cp == 0)
+ continue;
+
+ if (strncmp (cp, "pointdefs:", 10))
+ continue;
+
+ cp += 10;
+
+ /* skip ws after comment start */
+ while (*cp && isspace ((int)*cp))
+ cp++;
+
+ name = cp;
+
+ while (*cp && !isspace ((int)*cp))
+ cp++;
+
+ *cp = 0;
+
+ s_hfp = fopen (name, "rt");
+
+ if (s_hfp == NULL) {
+ g_warning ("Couldn't open header file %s\n", name);
+ continue;
+ }
+ rv = TRUE;
+
+ s_basenum = basenum;
+
+ read_header_file();
+
+ fclose (s_hfp);
+ }
+ return(rv);
+}
+
+/****************************************************************************
+* event_def_cmp
+****************************************************************************/
+
+int event_def_cmp(const void *a1, const void *a2)
+{
+ event_def_t *e1 = (event_def_t *)a1;
+ event_def_t *e2 = (event_def_t *)a2;
+
+ if (e1->event < e2->event)
+ return(-1);
+ else if (e1->event == e2->event)
+ return(0);
+ else
+ return(1);
+}
+
+/****************************************************************************
+* sort_event_definitions
+****************************************************************************/
+
+void sort_event_definitions(void)
+{
+ qsort(&g_eventdefs[0], g_neventdefs, sizeof(event_def_t), event_def_cmp);
+}
+
+static boolean remove_needed=TRUE;
+
+void finalize_events(void)
+{
+ sort_event_definitions();
+ create_point_selector();
+ recompute_ps_vscrollbar();
+ view1_display_when_idle();
+ remove_needed = TRUE;
+}
+
+void initialize_events(void)
+{
+ if (remove_needed) {
+ reset_point_selector();
+ remove_all_events();
+ remove_needed = FALSE;
+ }
+}
+
+/****************************************************************************
+* read_event_definitions
+****************************************************************************/
+
+boolean read_event_definitions (char *filename)
+{
+ char tmpbuf [128];
+
+ initialize_events();
+
+ s_elog_hfp = fopen (filename, "rt");
+ if (s_elog_hfp == NULL) {
+ sprintf (tmpbuf, "Couldn't open %s\n", filename);
+ infobox ("Open Failed", tmpbuf);
+ return(FALSE);
+ }
+ /* Presume "elog.h". Note fallthrough... */
+ if (read_header_files()) {
+ sort_event_definitions();
+ create_point_selector();
+ recompute_ps_vscrollbar();
+ fclose(s_elog_hfp);
+ view1_display_when_idle();
+ remove_needed = TRUE;
+ return(TRUE);
+ }
+ fclose(s_elog_hfp);
+
+ s_hfp = fopen (filename, "rt");
+ if (s_hfp == NULL) {
+ sprintf (tmpbuf, "Couldn't open %s\n", filename);
+ infobox ("Read Event Definition Failure", tmpbuf);
+ return(FALSE);
+ }
+
+ read_header_file();
+
+ /* Happens if the user feeds us the wrong file, for example */
+ if (g_neventdefs == 0) {
+ sprintf (tmpbuf, "No event definitions found in %s\n", filename);
+ infobox ("No Event Definitions?", tmpbuf);
+ return(FALSE);
+ }
+ finalize_events();
+ return(TRUE);
+}
+
+static event_def_t dummy_event;
+static char dummy_string[32];
+
+/****************************************************************************
+* find_event_definition
+* Binary search for first event whose time is >= t
+****************************************************************************/
+
+event_def_t *find_event_definition (ulong code)
+{
+ int index, bottom, top;
+ event_def_t *edp;
+
+ if (g_neventdefs == 0)
+ goto use_dummy;
+
+ bottom = g_neventdefs-1;
+ top = 0;
+
+ while (1) {
+ index = (bottom + top) / 2;
+
+ edp = (g_eventdefs + index);
+
+ if (edp->event == code)
+ return(edp);
+
+ if (top >= bottom) {
+ use_dummy:
+ edp = &dummy_event;
+ edp->selected = TRUE;
+ edp->event = code;
+ edp->format = "0x%x";
+ sprintf (dummy_string, "E%lu", code);
+ edp->name = &dummy_string[0];
+ return(edp);
+ }
+
+ if (edp->event < code)
+ top = index + 1;
+ else
+ bottom = index - 1;
+ }
+}
+
+/****************************************************************************
+* pointsel_next_snapshot
+* Set dialog buttons from snapshot
+****************************************************************************/
+
+void pointsel_next_snapshot(void)
+{
+ int i;
+
+ for (i = 0; i < g_neventdefs; i++) {
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON(s_event_buttons[i]),
+ g_eventdefs[i].selected);
+ }
+}
+
+/****************************************************************************
+* pointsel_about
+****************************************************************************/
+
+void pointsel_about (char *tmpbuf)
+{
+ sprintf (tmpbuf+strlen(tmpbuf), "%d event definitions\n",
+ g_neventdefs);
+}
diff --git a/g2/props.c b/g2/props.c
new file mode 100644
index 00000000000..a23dc0504d2
--- /dev/null
+++ b/g2/props.c
@@ -0,0 +1,279 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 1997-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <malloc.h>
+#include <time.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+static char *sxerox (char *s);
+void exit(int);
+
+#define NBUCKETS 97
+
+typedef struct prop_ {
+ struct prop_ *next;
+ char *name;
+ char *value;
+} prop_t;
+
+static prop_t *buckets [NBUCKETS];
+static int hash_shifts[4] = {24, 16, 8, 0};
+
+/*
+ * getprop
+ */
+
+char *getprop (char *name)
+{
+ unsigned char *cp;
+ unsigned long hash=0;
+ prop_t *bp;
+ int i=0;
+
+ for (cp = (unsigned char *) name; *cp; cp++)
+ hash ^= (*cp)<<(hash_shifts[(i++)&0x3]);
+
+ bp = buckets [hash%NBUCKETS];
+
+ while (bp && strcmp (bp->name, name)) {
+ bp = bp->next;
+ }
+
+ if (bp == NULL)
+ return (0);
+ else
+ return (bp->value);
+}
+
+/*
+ * getprop_default
+ */
+
+char *getprop_default (char *name, char *def)
+{
+ char *rv;
+ rv = getprop (name);
+ if (rv)
+ return (rv);
+ else
+ return (def);
+}
+
+/*
+ * addprop
+ */
+
+void addprop (char *name, char *value)
+{
+ unsigned char *cp;
+ unsigned long hash=0;
+ prop_t **bpp;
+ prop_t *bp;
+ int i=0;
+
+ bp = (prop_t *)g_malloc (sizeof (prop_t));
+
+ bp->next = 0;
+ bp->name = sxerox (name);
+ bp->value = sxerox (value);
+
+ for (cp = (unsigned char *)name; *cp; cp++)
+ hash ^= (*cp)<<(hash_shifts[(i++)&0x3]);
+
+ bpp = &buckets [hash%NBUCKETS];
+
+ if (*bpp == NULL)
+ *bpp = bp;
+ else {
+ bp->next = *bpp;
+ *bpp = bp;
+ }
+}
+
+/*
+ * sxerox
+ */
+
+static char *sxerox (char *s)
+{
+ char *rv = (char *) g_malloc (strlen (s) + 1);
+ strcpy (rv, s);
+ return rv;
+}
+
+/*
+ * readprops
+ */
+
+#define START 0
+#define READNAME 1
+#define READVALUE 2
+#define C_COMMENT 3
+#define CPP_COMMENT 4
+
+int readprops (char *filename)
+{
+ FILE *ifp;
+ unsigned char c;
+ int state=START;
+ int linenum=1;
+ char namebuf [128];
+ char valbuf [512];
+ int i;
+
+ ifp = fopen (filename, "r");
+
+ if (ifp == NULL)
+ return (-1);
+
+ while (1) {
+
+ readchar:
+ c = getc (ifp);
+
+ again:
+ switch (state) {
+ case START:
+ if (feof (ifp)) {
+ fclose (ifp);
+ return (0);
+ }
+
+ if (c == ' ' || c == '\t')
+ goto readchar;
+
+ if (c == '\n') {
+ linenum++;
+ goto readchar;
+ }
+ if (isalpha (c) || (c == '_')) {
+ state = READNAME;
+ goto again;
+ }
+ if (c == '/') {
+ c = getc (ifp);
+ if (c == '/') {
+ state = CPP_COMMENT;
+ goto readchar;
+ } else if (c == '*') {
+ state = C_COMMENT;
+ goto readchar;
+ } else {
+ fprintf (stderr, "unknown token '/' line %d\n",
+ linenum);
+ exit (1);
+ }
+ }
+ fprintf (stderr, "unknown token '%c' line %d\n",
+ c, linenum);
+ exit (1);
+ break;
+
+ case CPP_COMMENT:
+ while (1) {
+ c = getc (ifp);
+ if (feof (ifp))
+ return (0);
+ if (c == '\n') {
+ linenum++;
+ state = START;
+ goto readchar;
+ }
+ }
+ break;
+
+ case C_COMMENT:
+ while (1) {
+ c = getc (ifp);
+ if (feof (ifp)) {
+ fprintf (stderr, "unterminated comment, line %d\n",
+ linenum);
+ exit (1);
+ }
+ if (c == '*') {
+ staragain:
+ c = getc (ifp);
+ if (c == '/') {
+ state = START;
+ goto readchar;
+ }
+ if (c == '*')
+ goto staragain;
+ }
+ }
+ break;
+
+ case READNAME:
+ i = 0;
+ namebuf[i++] = c;
+ while (1) {
+ c = getc (ifp);
+ if (feof (ifp)) {
+ fprintf (stderr, "EOF while reading a name, line %d\n",
+ linenum);
+ exit (1);
+ }
+ if ((!isalnum (c)) && (c != '_')) {
+ namebuf [i] = 0;
+ state = READVALUE;
+ goto again;
+ }
+ namebuf [i++] = c;
+ }
+ break;
+
+ case READVALUE:
+ i = 0;
+ while ((c == ' ') || (c == '\t') || (c == '=')) {
+ c = getc (ifp);
+ if (feof (ifp)) {
+ fprintf (stderr, "EOF while reading a value, line %d\n",
+ linenum);
+ exit (1);
+ }
+ }
+ goto firsttime;
+ while (1) {
+ c = getc (ifp);
+
+ firsttime:
+ if (c == '\\') {
+ c = getc (ifp);
+ if (feof (ifp)) {
+ fprintf (stderr, "EOF after '\\', line %d\n",
+ linenum);
+ exit (1);
+ }
+ valbuf[i++] = c;
+ continue;
+ }
+ if (c == '\n') {
+ linenum++;
+ while (valbuf [i-1] == ' ' || valbuf[i-1] == '\t')
+ i--;
+ valbuf[i] = 0;
+ addprop (namebuf, valbuf);
+ state = START;
+ goto readchar;
+ }
+ valbuf[i++] = c;
+ }
+
+ }
+ }
+}
diff --git a/g2/props.h b/g2/props.h
new file mode 100644
index 00000000000..6289941dd4d
--- /dev/null
+++ b/g2/props.h
@@ -0,0 +1,21 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 1997-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern char *getprop (char *name);
+extern char *getprop_default (char *name, char *def);
+extern void addprop (char *name, char *value);
+extern int readprops (char *filename);
+extern int writeprops (char *filename);
diff --git a/g2/view1.c b/g2/view1.c
new file mode 100644
index 00000000000..ec394cc3d53
--- /dev/null
+++ b/g2/view1.c
@@ -0,0 +1,3077 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2005-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "g2.h"
+#include <time.h>
+#include <string.h>
+#include <vppinfra/format.h>
+#include <vppinfra/elog.h>
+
+/*
+ * The main event display view.
+ *
+ * Important variables:
+ *
+ * "da" -- the drawing area, aka the screen representation of the
+ * event view.
+ *
+ * "pm" -- the backing pixmap for the drawing area. Note that
+ * all graphics operations target this backing
+ * store, then call gtk_widget_draw to copy a rectangle from
+ * the backing store onto the screen.
+ *
+ * "s_v1" -- pointer to the current v1_geometry_t object.
+ *
+ * Box heirarchy:
+ * s_view1_vbox
+ * s_view1_hbox
+ * da s_view1_vmenubox
+ * s_view1_topbutton("Top")
+ * s_view1_vscroll (vertical scrollbar)
+ * s_view1_bottombutton("Bottom")
+ * s_view1_hmenubox
+ * s_view1_startbutton("Start");
+ * s_view1_hscroll(horizontal scrollbar)
+ * s_view1_endbutton("End")
+ * s_view1_zoominbutton("Zoomin")
+ * s_view1_searchbutton("Search")
+ * s_view1_searchagainbutton("Search Again")
+ * s_view1_zoomoutbutton("Zoomout")
+ * s_view1_label
+ */
+
+/*
+ * Globals
+ */
+
+GdkFont *g_font; /* a fixed-width font to use */
+GdkColor fg_black = {0, 0, 0, 0};
+GdkColor bg_white = {0, 65535, 65535, 65535};
+static boolean summary_mode = TRUE; /* start out in summary mode */
+static boolean color_mode = FALSE; /* start out in color mode */
+
+/*
+ * Locals
+ */
+
+/*
+ * user_data values passed to view1_button_click_callback,
+ * which is used by the various action buttons noted above
+ */
+enum view1_button_click {
+ TOP_BUTTON=1,
+ BOTTOM_BUTTON,
+ START_BUTTON,
+ ZOOMIN_BUTTON,
+ SEARCH_BUTTON,
+ SEARCH_AGAIN_BUTTON,
+ ZOOMOUT_BUTTON,
+ END_BUTTON,
+ MORE_TRACES_BUTTON,
+ LESS_TRACES_BUTTON,
+ SNAP_BUTTON,
+ NEXT_BUTTON,
+ DEL_BUTTON,
+ CHASE_EVENT_BUTTON,
+ CHASE_DATUM_BUTTON,
+ CHASE_TRACK_BUTTON,
+ UNCHASE_BUTTON,
+ FORWARD_BUTTON,
+ BACKWARD_BUTTON,
+ SUMMARY_BUTTON,
+ NOSUMMARY_BUTTON,
+};
+
+enum chase_mode {
+ CHASE_EVENT=1,
+ CHASE_DATUM,
+ CHASE_TRACK,
+};
+
+enum sc_dir {
+ SRCH_CHASE_FORWARD = 0,
+ SRCH_CHASE_BACKWARD = 1,
+};
+
+static GtkWidget *s_view1_hbox; /* see box heirarchy chart */
+static GtkWidget *s_view1_vbox; /* see box heirarchy chart */
+static GtkWidget *da; /* main drawing area */
+static GdkPixmap *pm; /* and its backing pixmap */
+static GdkCursor *norm_cursor; /* the "normal" cursor */
+
+/*
+ * view geometry parameters
+ *
+ * Remember:
+ * Y increases down the page.
+ * Strip origin is at the top
+ * Payday is Friday
+ * Don't put your fingers in your mouth.
+ *
+ * Most of these values are in pixels
+ */
+
+typedef struct v1_geometry {
+ int pid_ax_width; /* Width of the PID axis */
+ int time_ax_height; /* Height of the time axis */
+ int time_ax_spacing; /* TimeAxis: Space between tick-marks */
+ int strip_height; /* Height of a regular PID trace */
+ int pop_offset; /* Vertical offset of the detail box */
+ int pid_ax_offset; /* Vertical offset of the PID axis */
+ int event_offset; /* Vertical offset of the event boxes */
+ int total_height; /* total height of da, see configure_event */
+ int total_width; /* ditto, for width */
+
+ /* Derived values */
+ int first_pid_index; /* Index of first displayed PID */
+ int npids; /* Max number of displayed pids */
+ ulonglong minvistime; /* in usec */
+ ulonglong maxvistime; /* in usec */
+} v1_geometry_t;
+
+
+/* The active geometry object */
+static v1_geometry_t s_v1record;
+static v1_geometry_t *s_v1 = &s_v1record;
+
+/* The color array */
+static GdkColor *s_color;
+
+/* Snapshot ring */
+typedef struct snapshot {
+ struct snapshot *next;
+ /* Screen geometry */
+ v1_geometry_t geometry;
+ boolean show_event[NEVENTS];
+ pid_sort_t *pidvec;
+ /*
+ * Note: not worth recomputing the vertical scrollbar, just save
+ * its value here
+ */
+ gfloat vscroll_value;
+ boolean summary_mode;
+ boolean color_mode;
+} snapshot_t;
+
+static snapshot_t *s_snapshots;
+static snapshot_t *s_cursnap;
+static event_t *s_last_selected_event;
+
+/*
+ * various widgets, see the box heirarchy chart above
+ * The toolkit keeps track of these things, we could lose many of
+ * these pointers.
+ */
+static GtkWidget *s_view1_vmenubox;
+static GtkWidget *s_view1_topbutton;
+static GtkWidget *s_view1_bottombutton;
+static GtkWidget *s_view1_more_traces_button;
+static GtkWidget *s_view1_less_traces_button;
+
+static GtkWidget *s_view1_hmenubox;
+static GtkWidget *s_view1_hmenubox2;
+static GtkWidget *s_view1_startbutton;
+static GtkWidget *s_view1_zoominbutton;
+static GtkWidget *s_view1_searchbutton;
+static GtkWidget *s_view1_srchagainbutton;
+static GtkWidget *s_view1_zoomoutbutton;
+static GtkWidget *s_view1_endbutton;
+
+static GtkWidget *s_view1_snapbutton;
+static GtkWidget *s_view1_nextbutton;
+static GtkWidget *s_view1_delbutton;
+
+static GtkWidget *s_view1_chase_event_button;
+static GtkWidget *s_view1_chase_datum_button;
+static GtkWidget *s_view1_chase_track_button;
+static GtkWidget *s_view1_unchasebutton;
+
+static GtkWidget *s_view1_forward_button;
+static GtkWidget *s_view1_backward_button;
+
+static GtkWidget *s_view1_summary_button;
+static GtkWidget *s_view1_nosummary_button;
+
+static GtkWidget *s_view1_hscroll;
+static GtkObject *s_view1_hsadj;
+
+static GtkWidget *s_view1_vscroll;
+static GtkObject *s_view1_vsadj;
+
+static GtkWidget *s_view1_label;
+
+/*
+ * Search context
+ */
+static ulong s_srchcode; /* search event code */
+static int s_srchindex; /* last hit was at this event index */
+static boolean s_result_up; /* The SEARCH RESULT dongle is displayed */
+static boolean s_srchfail_up; /* The status line "Search Failed" is up */
+static int srch_chase_dir; /* search/chase dir, 0=>forward */
+
+
+/*
+ * Print context
+ */
+static int s_print_offset; /* Magic offset added to line, tbox fn codes */
+static FILE *s_printfp;
+
+/*
+ * Forward reference prototypes
+ */
+static void display_pid_axis(v1_geometry_t *vp);
+static void display_event_data(v1_geometry_t *vp);
+static void display_time_axis(v1_geometry_t *vp);
+static void view1_button_click_callback(GtkButton *item, gpointer data);
+
+/*
+ * config params
+ */
+
+gint c_view1_draw_width;
+gint c_view1_draw_height;
+
+/*
+ * Zoom-In / Time Ruler cursor
+ */
+
+#define zi_width 32
+#define zi_height 32
+#define zi_x_hot 22
+#define zi_y_hot 14
+static unsigned char zi_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x88, 0x00,
+ 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xc0, 0x00,
+ 0x00, 0xfc, 0xff, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xa0, 0x00,
+ 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x84, 0x00,
+ 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+static unsigned char zi_bkgd[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x88, 0x00,
+ 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xc0, 0x00,
+ 0x00, 0xfc, 0xff, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xa0, 0x00,
+ 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x84, 0x00,
+ 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+static GdkCursor *zi_cursor;
+static GdkPixmap *zi_source, *zi_mask;
+
+/*
+ * Frequently-used small computations, best
+ * done correctly once and instantiated.
+ */
+
+/****************************************************************************
+* dtime_per_pixel
+****************************************************************************/
+
+static inline double dtime_per_pixel(v1_geometry_t *vp)
+{
+ return ((double)(vp->maxvistime - vp->minvistime)) /
+ ((double)(vp->total_width - vp->pid_ax_width));
+}
+
+/****************************************************************************
+* message_line
+* Changes the status line. Pass "" to clear the status line.
+****************************************************************************/
+
+void message_line (char *s)
+{
+ gtk_label_set_text (GTK_LABEL(s_view1_label), s);
+}
+
+/****************************************************************************
+* set_window_title
+* Changes the window title to include the specified filename.
+****************************************************************************/
+
+void set_window_title (const char *filename)
+{
+ char title[128];
+ snprintf(title, sizeof(title), "g2 (%s)", filename);
+ gtk_window_set_title(GTK_WINDOW(g_mainwindow), title);
+}
+
+/****************************************************************************
+* recompute_hscrollbar
+* Adjust the horizontal scrollbar's adjustment object.
+*
+* GtkAdjustments are really cool, but have to be set up exactly
+* right or the various client objects screw up completely.
+*
+* Note: this function is *not* called when the user clicks the scrollbar.
+****************************************************************************/
+
+static void recompute_hscrollbar (void)
+{
+ ulonglong current_width;
+ ulonglong event_incdec;
+ GtkAdjustment *adj;
+ event_t *ep;
+
+ if (g_nevents == 0)
+ return;
+
+ ep = (g_events + (g_nevents-1));
+ current_width = s_v1->maxvistime - s_v1->minvistime;
+ event_incdec = (current_width) / 6;
+
+ adj = GTK_ADJUSTMENT(s_view1_hsadj);
+
+ /*
+ * Structure member decoder ring
+ * -----------------------------
+ * lower the minimum possible value
+ * value the current value
+ * upper the maximum possible value
+ * step_increment end button click increment
+ * page_increment click in trough increment
+ * page_size size of currently visible area
+ */
+
+ adj->lower = (gfloat)0.00;
+ adj->value = (gfloat)s_v1->minvistime;
+
+ /* Minor click: move about 1/6 of a page */
+ adj->step_increment = (gfloat)event_incdec;
+
+ /* Major click: move about 1/3 of a page. */
+ adj->page_increment = (gfloat)(2*event_incdec);
+
+ /* allow the user to go a bit past the end */
+ adj->upper = adj->page_increment/3 + (gfloat)(ep->time);
+ adj->page_size = (gfloat)(current_width);
+
+ /*
+ * Tell all clients (e.g. the visible scrollbar) to
+ * make themselves look right
+ */
+ gtk_adjustment_changed(adj);
+ gtk_adjustment_value_changed(adj);
+}
+
+/****************************************************************************
+* recompute_vscrollbar
+* Ditto, for the vertical scrollbar
+****************************************************************************/
+
+static void recompute_vscrollbar (void)
+{
+ GtkAdjustment *adj;
+
+ adj = GTK_ADJUSTMENT(s_view1_vsadj);
+
+ adj->lower = (gfloat)0.00;
+ adj->upper = (gfloat)g_npids;
+ adj->value = (gfloat)0.00;
+ adj->step_increment = 1.00;
+ adj->page_increment = (gfloat)(s_v1->npids / 3);
+ adj->page_size = (gfloat)s_v1->npids;
+ gtk_adjustment_changed(adj);
+ gtk_adjustment_value_changed(adj);
+}
+
+/****************************************************************************
+* format_popbox_string
+****************************************************************************/
+
+elog_main_t elog_main;
+
+void format_popbox_string (char *tmpbuf, int len, event_t *ep, event_def_t *edp)
+{
+ char *fp;
+
+#ifdef NOTDEF
+ sprintf(tmpbuf,"%d:", ep->code);
+#endif
+ if (ep->flags & EVENT_FLAG_CLIB) {
+ elog_event_t *eep;
+ u8 *s;
+
+ eep = get_clib_event (ep->datum);
+
+ s = format (0, "%U", format_elog_event, &elog_main, eep);
+ memcpy (tmpbuf, s, vec_len(s));
+ tmpbuf[vec_len(s)] = 0;
+ vec_free(s);
+ return;
+ }
+
+ snprintf(tmpbuf, len, "%s", edp->name);
+ fp = edp->format;
+ /* Make sure there's a real format string. If so, add it */
+ while (fp && *fp) {
+ if (*fp != ' ') {
+ snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf), ": ");
+ /* %s only supported for cpel files */
+ if (fp[1] == 's') {
+ snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf),
+ edp->format, strtab_ref(ep->datum));
+ } else {
+ snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf),
+ edp->format, ep->datum);
+ }
+ return;
+ }
+ fp++;
+ }
+}
+
+/****************************************************************************
+ * add_snapshot
+ ****************************************************************************/
+
+static void add_snapshot(void)
+{
+ int i;
+ snapshot_t *new = g_malloc(sizeof(snapshot_t));
+
+ memcpy(&new->geometry, s_v1, sizeof(new->geometry));
+ for (i = 0; i < NEVENTS; i++) {
+ new->show_event[i] = g_eventdefs[i].selected;
+ }
+ new->pidvec = g_malloc(sizeof(pid_sort_t)*g_npids);
+ memcpy(new->pidvec, g_pids, sizeof(pid_sort_t)*g_npids);
+ new->vscroll_value = GTK_ADJUSTMENT(s_view1_vsadj)->value;
+ new->summary_mode = summary_mode;
+ new->color_mode = color_mode;
+
+ if (s_snapshots) {
+ new->next = s_snapshots;
+ s_snapshots = new;
+ } else {
+ new->next = 0;
+ s_snapshots = new;
+ }
+ s_cursnap = new;
+}
+
+/****************************************************************************
+ * next_snapshot
+ ****************************************************************************/
+
+static void next_snapshot(void)
+{
+ snapshot_t *next;
+ int i;
+ pid_sort_t *psp;
+ pid_data_t *pp;
+
+ if (!s_snapshots) {
+ infobox("No snapshots", "\nNo snapshots in the ring...\n");
+ return;
+ }
+
+ next = s_cursnap->next;
+ if (next == 0)
+ next = s_snapshots;
+
+ s_cursnap = next;
+
+ memcpy(s_v1, &next->geometry, sizeof(next->geometry));
+ for (i = 0; i < NEVENTS; i++) {
+ g_eventdefs[i].selected = next->show_event[i];
+ }
+ memcpy(g_pids, next->pidvec, sizeof(pid_sort_t)*g_npids);
+ color_mode = next->color_mode;
+ /*
+ * Update summary mode via a button push so that the button state is
+ * updated accordingly. (Should ideally clean up the view/controller
+ * separation properly one day.)
+ */
+ if (summary_mode != next->summary_mode) {
+ view1_button_click_callback
+ (NULL, (gpointer)(unsigned long long)
+ (summary_mode ? NOSUMMARY_BUTTON : SUMMARY_BUTTON));
+ }
+
+ /* Fix the pid structure index mappings */
+ psp = g_pids;
+
+ for (i = 0; i < g_npids; i++) {
+ pp = psp->pid;
+ pp->pid_index = i;
+ psp++;
+ }
+ GTK_ADJUSTMENT(s_view1_vsadj)->value = next->vscroll_value;
+ gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
+ recompute_hscrollbar();
+ pointsel_next_snapshot();
+ view1_display_when_idle();
+}
+
+
+/****************************************************************************
+ * del_snapshot
+ ****************************************************************************/
+
+static void del_snapshot(void)
+{
+ snapshot_t *prev;
+ snapshot_t *this;
+
+ if (!s_snapshots) {
+ infobox("No snapshots", "\nNo snapshots to delete...\n");
+ return;
+ }
+
+ prev = NULL;
+ this = s_snapshots;
+
+ while (this && this != s_cursnap) {
+ prev = this;
+ this = this->next;
+ }
+
+ if (this != s_cursnap) {
+ infobox("BUG", "\nSnapshot AWOL!\n");
+ return;
+ }
+
+ s_cursnap = this->next;
+
+ /* middle of the list? */
+ if (prev) {
+ prev->next = this->next;
+ g_free(this->pidvec);
+ g_free(this);
+ } else { /* start of the list */
+ s_snapshots = this->next;
+ g_free(this->pidvec);
+ g_free(this);
+ }
+
+ /* Note: both will be NULL after last delete */
+ if (s_cursnap == NULL)
+ s_cursnap = s_snapshots;
+}
+
+/****************************************************************************
+ * write_snapshot
+ *
+ * VERY primitive right now - not endian or version independent, and only
+ * writes to "snapshots.g2" in the current directory
+ ****************************************************************************/
+static void write_snapshot(void)
+{
+ FILE *file = NULL;
+ snapshot_t *snap;
+ char *error = NULL;
+ int records = 0;
+
+ if (s_snapshots == NULL) {
+ error = "No snapshots defined";
+ errno = 0;
+ }
+
+ if (!error) {
+ file = fopen("snapshots.g2", "w");
+ if (file == NULL) {
+ error = "Unable to open snapshots.g2";
+ }
+ }
+
+ /*
+ * Simply serialize the arch-dependent binary data, without a care in the
+ * world. Don't come running to me if you try to read it and crash.
+ */
+ for (snap = s_snapshots; !error && snap != NULL; snap = snap->next) {
+ if (fwrite(&snap->geometry,
+ sizeof(snap->geometry), 1, file) != 1 ||
+ fwrite(&snap->show_event,
+ sizeof(snap->show_event), 1, file) != 1 ||
+ fwrite(snap->pidvec,
+ sizeof(pid_sort_t) * g_npids, 1, file) != 1 ||
+ fwrite(&snap->vscroll_value,
+ sizeof(snap->vscroll_value), 1, file) != 1 ||
+ fwrite(&snap->summary_mode,
+ sizeof(snap->summary_mode), 1, file) != 1 ||
+ fwrite(&snap->color_mode,
+ sizeof(snap->color_mode), 1, file) != 1) {
+ error = "Error writing data";
+ }
+ records++;
+ }
+
+ if (!error) {
+ if (fclose(file)) {
+ error = "Unable to close file";
+ }
+ }
+
+ if (error) {
+ infobox(error, strerror(errno));
+ } else {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "Wrote %d snapshots to snapshots.g2",
+ records);
+ message_line(buf);
+ }
+}
+
+/****************************************************************************
+ * read_snapshot
+ *
+ * VERY primitive right now - not endian or version independent, and only reads
+ * from "snapshots.g2" in the current directory
+ ****************************************************************************/
+static void read_snapshot(void)
+{
+ FILE *file;
+ snapshot_t *snap, *next_snap;
+ snapshot_t *new_snaps = NULL;
+ char *error = NULL;
+ int len, i, records = 0;
+ pid_data_t *pp;
+
+ file = fopen("snapshots.g2", "r");
+ if (file == NULL) {
+ error = "Unable to open snapshots.g2";
+ }
+
+ /*
+ * Read in the snapshots and link them together. We insert them backwards,
+ * but that's tolerable. If the data is in anyway not what we expect, we'll
+ * probably crash. Sorry.
+ */
+ while (!error && !feof(file)) {
+ snap = g_malloc(sizeof(*snap));
+ snap->pidvec = NULL; /* so we can free this if there's an error */
+
+ len = fread(&snap->geometry, sizeof(snap->geometry), 1, file);
+ if (len == 0) {
+ /* EOF */
+ g_free(snap);
+ break;
+ } else {
+ /* insert into list straight away */
+ snap->next = new_snaps;
+ new_snaps = snap;
+ }
+ if (len != 1) {
+ error = "Problem reading first item from file";
+ break;
+ }
+ if (fread(&snap->show_event, sizeof(snap->show_event), 1, file) != 1) {
+ error = "Problem reading second item from file";
+ break;
+ }
+ len = sizeof(pid_sort_t) * g_npids;
+ snap->pidvec = g_malloc(len);
+ if (fread(snap->pidvec, len, 1, file) != 1) {
+ error = "Problem reading third item from file";
+ break;
+ }
+ if (fread(&snap->vscroll_value,
+ sizeof(snap->vscroll_value), 1, file) != 1 ||
+ fread(&snap->summary_mode,
+ sizeof(snap->summary_mode), 1, file) != 1 ||
+ fread(&snap->color_mode,
+ sizeof(snap->color_mode), 1, file) != 1) {
+ error = "Problem reading final items from file";
+ break;
+ }
+
+ /*
+ * Fix up the pointers from the sorted pid vector back into our pid
+ * data objects, by walking the linked list of pid_data_t objects for
+ * every one looking for a match. This is O(n^2) grossness, but in real
+ * life there aren't that many pids, and it seems zippy enough.
+ */
+ for (i = 0; i < g_npids; i++) {
+ for (pp = g_pid_data_list; pp != NULL; pp = pp->next) {
+ if (pp->pid_value == snap->pidvec[i].pid_value) {
+ break;
+ }
+ }
+ if (pp != NULL) {
+ snap->pidvec[i].pid = pp;
+ } else {
+ error = "Snapshot file referenced unknown pids";
+ break;
+ }
+ }
+
+ records++;
+ }
+
+ if (!error) {
+ if (fclose(file)) {
+ error = "Unable to close file";
+ }
+ }
+
+ if (error) {
+ /*
+ * Problem - clear up any detritus
+ */
+ infobox(error, strerror(errno));
+ for (snap = new_snaps; snap != NULL; snap = next_snap) {
+ next_snap = snap->next;
+ g_free(snap);
+ g_free(snap->pidvec);
+ }
+ } else {
+ /*
+ * Success! trash the old snapshots and replace with the new
+ */
+ for (snap = s_snapshots; snap != NULL; snap = next_snap) {
+ next_snap = snap->next;
+ g_free(snap->pidvec);
+ g_free(snap);
+ }
+
+ s_cursnap = s_snapshots = new_snaps;
+ }
+
+ if (error) {
+ infobox(error, strerror(errno));
+ } else {
+ char buf[64];
+ snprintf(buf, sizeof(buf),
+ "Read %d snapshots from snapshots.g2", records);
+ message_line(buf);
+ }
+}
+
+/****************************************************************************
+* set_color
+*
+* Set the color for the specified pid_index, or COLOR_DEFAULT to return it
+* to the usual black.
+****************************************************************************/
+#define COLOR_DEFAULT (-1)
+static void set_color(int pid_index)
+{
+ if (pid_index == COLOR_DEFAULT || !color_mode) {
+ gdk_gc_set_foreground(da->style->black_gc, &fg_black);
+ } else {
+ gdk_gc_set_foreground(da->style->black_gc,
+ &s_color[g_pids[pid_index].color_index]);
+ }
+}
+
+/****************************************************************************
+* toggle_event_select
+****************************************************************************/
+
+static void toggle_event_select(GdkEventButton *event, v1_geometry_t *vp)
+{
+ int pid_index, start_index;
+ int x, y;
+ GdkRectangle *rp;
+ GdkRectangle hit_rect;
+ GdkRectangle dummy;
+ event_t *ep;
+ event_def_t *edp;
+ char tmpbuf [1024];
+ double time_per_pixel;
+
+ if (g_nevents == 0)
+ return;
+
+ time_per_pixel = dtime_per_pixel(vp);
+
+ start_index = find_event_index (vp->minvistime);
+
+ /* Too far right? */
+ if (start_index >= g_nevents)
+ return;
+
+ /*
+ * To see if the mouse hit a visible event, use a variant
+ * of the event display loop.
+ */
+
+ hit_rect.x = (int)event->x;
+ hit_rect.y = (int)event->y;
+ hit_rect.width = 1;
+ hit_rect.height = 1;
+
+ ep = (g_events + start_index);
+
+ while ((ep->time < vp->maxvistime) &&
+ (ep < (g_events + g_nevents))) {
+ pid_index = ep->pid->pid_index;
+
+ /* First filter: pid out of range */
+ if ((pid_index < vp->first_pid_index) ||
+ (pid_index >= vp->first_pid_index + vp->npids)) {
+ ep++;
+ continue;
+ }
+
+ /* Second filter: event hidden */
+ edp = find_event_definition (ep->code);
+ if (!edp->selected) {
+ ep++;
+ continue;
+ }
+
+ /*
+ * At this point, we know that the point is at least on the
+ * screen. See if the mouse hit within the bounding box
+ */
+
+ /*
+ * $$$$ maybe keep looping until off the edge,
+ * maintain a "best hit", then declare that one the winner?
+ */
+
+ pid_index -= vp->first_pid_index;
+
+ y = pid_index*vp->strip_height + vp->event_offset;
+
+ x = vp->pid_ax_width +
+ (int)(((double)(ep->time - vp->minvistime)) / time_per_pixel);
+
+ /* Perhaps we're trying to toggle the detail box? */
+ if (ep->flags & EVENT_FLAG_SELECT) {
+ /* Figure out the dimensions of the detail box */
+ format_popbox_string(tmpbuf, sizeof(tmpbuf), ep, edp);
+ rp = tbox(tmpbuf, x, y - vp->pop_offset, TBOX_GETRECT_BOXED);
+ if (gdk_rectangle_intersect(rp, &hit_rect, &dummy)) {
+ ep->flags &= ~EVENT_FLAG_SELECT;
+ view1_display_when_idle();
+ break;
+ }
+ }
+
+ sprintf(tmpbuf, "%ld", ep->code);
+
+ /* Figure out the dimensions of the regular box */
+ rp = tbox(tmpbuf, x, y, TBOX_GETRECT_EVENT);
+
+ if (gdk_rectangle_intersect(rp, &hit_rect, &dummy)) {
+ /* we hit the rectangle. */
+ if (ep->flags & EVENT_FLAG_SELECT) {
+ ep->flags &= ~EVENT_FLAG_SELECT;
+ view1_display_when_idle();
+ break;
+ } else {
+ set_color(ep->pid->pid_index);
+
+ /* It wasn't selected, so put up the detail box */
+ format_popbox_string(tmpbuf, sizeof(tmpbuf), ep, edp);
+ tbox(tmpbuf, x, y - vp->pop_offset, TBOX_DRAW_BOXED);
+ line(x, y-vp->pop_offset, x, y, LINE_DRAW_BLACK);
+ ep->flags |= EVENT_FLAG_SELECT;
+ ep->flags &= ~EVENT_FLAG_SEARCHRSLT;
+ s_last_selected_event = ep;
+ }
+ break;
+ }
+ ep++;
+ }
+}
+
+/****************************************************************************
+* move_current_track
+****************************************************************************/
+
+typedef enum { MOVE_TOP, MOVE_BOTTOM } move_type;
+
+static void move_current_track(GdkEventButton *event,
+ v1_geometry_t *vp,
+ move_type type)
+{
+ int i;
+ int pid_index;
+ int y, delta_y;
+ pid_sort_t *new_pidvec;
+ pid_sort_t *psp;
+ pid_sort_t *pold, *pnew;
+ pid_data_t *pp;
+
+ if (g_nevents == 0)
+ return;
+
+ /* Scan pid/track axis locations, looking for a match */
+ for (i = 0; i < vp->npids; i++) {
+ y = i*vp->strip_height + vp->pid_ax_offset;
+ delta_y = y - event->y;
+ if (delta_y < 0)
+ delta_y = -delta_y;
+ if (delta_y < 10) {
+ goto found;
+ }
+
+ }
+ infobox("NOTE", "\nNo PID/Track In Range\nPlease Try Again");
+ return;
+
+ found:
+ pid_index = i + vp->first_pid_index;
+
+ new_pidvec = g_malloc(sizeof(pid_sort_t)*g_npids);
+ pold = g_pids;
+ pnew = new_pidvec;
+
+ if (type == MOVE_TOP) {
+ /* move to top */
+ *pnew++ = g_pids[pid_index];
+ for (i = 0; i < pid_index; i++)
+ *pnew++ = *pold++;
+ pold++;
+ i++;
+ for (; i < g_npids; i++)
+ *pnew++ = *pold++;
+ } else {
+ /* move to bottom */
+ for (i = 0; i < pid_index; i++)
+ *pnew++ = *pold++;
+ pold++;
+ i++;
+ for (; i < g_npids; i++)
+ *pnew++ = *pold++;
+ *pnew = g_pids[pid_index];
+ }
+
+ g_free(g_pids);
+ g_pids = new_pidvec;
+
+ /*
+ * Revert the pid_index mapping to an identity map,
+ */
+ psp = g_pids;
+
+ for (i = 0; i < g_npids; i++) {
+ pp = psp->pid;
+ pp->pid_index = i;
+ psp++;
+ }
+ view1_display_when_idle();
+}
+
+/****************************************************************************
+* zoom_event
+* Process a zoom gesture. The use of doubles is required to avoid
+* truncating the various variable values, which in turn would lead to
+* some pretty random-looking zoom responses.
+****************************************************************************/
+
+void zoom_event(GdkEventButton *e1, GdkEventButton *e2, v1_geometry_t *vp)
+{
+ double xrange;
+ double time_per_pixel;
+ double width_in_pixels;
+ double center_on_time, width_in_time;
+ double center_on_pixel;
+
+ /*
+ * Clip the zoom area to the event display area.
+ * Otherwise, center_on_time - width_in_time is in hyperspace
+ * to the left of zero
+ */
+
+ if (e1->x < vp->pid_ax_width)
+ e1->x = vp->pid_ax_width;
+
+ if (e2->x < vp->pid_ax_width)
+ e2->x = vp->pid_ax_width;
+
+ if (e2->x == e1->x)
+ goto loser_zoom_repaint;
+
+ xrange = (double) (e2->x - e1->x);
+ if (xrange < 0.00)
+ xrange = -xrange;
+
+ /* Actually, width in pixels of half the zoom area */
+ width_in_pixels = xrange / 2.00;
+ time_per_pixel = dtime_per_pixel(vp);
+ width_in_time = width_in_pixels * time_per_pixel;
+
+ /* Center the screen on the center of the zoom area */
+ center_on_pixel = (double)((e2->x + e1->x) / 2.00) -
+ (double)vp->pid_ax_width;
+ center_on_time = center_on_pixel*time_per_pixel + (double)vp->minvistime;
+
+ /*
+ * Transform back to 64-bit integer microseconds, reset the
+ * scrollbar, schedule a repaint.
+ */
+ vp->minvistime = (ulonglong)(center_on_time - width_in_time);
+ vp->maxvistime = (ulonglong)(center_on_time + width_in_time);
+
+loser_zoom_repaint:
+ recompute_hscrollbar();
+
+ view1_display_when_idle();
+}
+
+/****************************************************************************
+* scroll_y
+*
+* Scroll up or down by the specified delta
+*
+****************************************************************************/
+static void scroll_y(int delta)
+{
+ int new_index = s_v1->first_pid_index + delta;
+ if (new_index + s_v1->npids > g_npids)
+ new_index = g_npids - s_v1->npids;
+ if (new_index < 0)
+ new_index = 0;
+
+ if (new_index != s_v1->first_pid_index) {
+ s_v1->first_pid_index = new_index;
+ GTK_ADJUSTMENT(s_view1_vsadj)->value = (gdouble)new_index;
+ gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
+ view1_display_when_idle();
+ }
+}
+
+/****************************************************************************
+* view1_handle_key_press_event
+* Relevant definitions in: /usr/include/gtk-1.2/gdk/gdktypes.h
+*
+* This routine implements hotkeys for the Quake generation:
+*
+* W - zoom in
+* S - zoom out
+* A - pan left
+* D - pan right
+* R - pan up
+* F - pan down
+* T - more traces
+* G - fewer traces
+*
+* E - toggle summary mode
+* C - toggle color mode
+*
+* X - take snapshot
+* Z - next snapshot
+* P - persist snapshots to file
+* L - load snapshots from file
+*
+* ctrl-Q - exit
+*
+****************************************************************************/
+gint
+view1_handle_key_press_event (GtkWidget *widget, GdkEventKey *event)
+{
+ long long delta;
+
+ switch (event->keyval) {
+ case GDK_w: // zoom in
+ view1_button_click_callback(NULL, (gpointer)ZOOMIN_BUTTON);
+ break;
+
+ case GDK_s: // zoom out
+ view1_button_click_callback(NULL, (gpointer)ZOOMOUT_BUTTON);
+ break;
+
+ case GDK_a: // pan left
+ delta = (s_v1->maxvistime - s_v1->minvistime) / 6;
+ if (s_v1->minvistime < delta) {
+ delta = s_v1->minvistime;
+ }
+ s_v1->minvistime -= delta;
+ s_v1->maxvistime -= delta;
+ recompute_hscrollbar();
+ break;
+
+ case GDK_d: // pan right
+ delta = (s_v1->maxvistime - s_v1->minvistime) / 6;
+ if (s_v1->maxvistime + delta > g_events[g_nevents - 1].time) {
+ /*
+ * @@@ this doesn't seem to quite reach the far right hand
+ * side correctly - not sure why.
+ */
+ delta = g_events[g_nevents - 1].time - s_v1->maxvistime;
+ }
+ s_v1->minvistime += delta;
+ s_v1->maxvistime += delta;
+ recompute_hscrollbar();
+ break;
+
+ case GDK_r: // pan up
+ scroll_y(-1);
+ break;
+
+ case GDK_f: // pan down
+ scroll_y(+1);
+ break;
+
+ case GDK_t: // fewer tracks
+ view1_button_click_callback(NULL, (gpointer)LESS_TRACES_BUTTON);
+ break;
+
+ case GDK_g: // more tracks
+ view1_button_click_callback(NULL, (gpointer)MORE_TRACES_BUTTON);
+ break;
+
+ case GDK_e: // toggle summary mode
+ view1_button_click_callback
+ (NULL, (gpointer)(unsigned long long)
+ (summary_mode ? NOSUMMARY_BUTTON : SUMMARY_BUTTON));
+ break;
+
+ case GDK_c: // toggle color mode
+ color_mode ^= 1;
+ view1_display_when_idle();
+ break;
+
+ case GDK_p: // persist snapshots
+ write_snapshot();
+ break;
+
+ case GDK_l: // load snapshots
+ read_snapshot();
+ break;
+
+ case GDK_x: // take snapshot
+ view1_button_click_callback(NULL, (gpointer)SNAP_BUTTON);
+ break;
+
+ case GDK_z: // next snapshot
+ view1_button_click_callback(NULL, (gpointer)NEXT_BUTTON);
+ break;
+
+ case GDK_q: // ctrl-q is exit
+ if (event->state & GDK_CONTROL_MASK) {
+ gtk_main_quit();
+ }
+ break;
+ }
+ return TRUE;
+}
+
+/****************************************************************************
+* button_press_event
+* Relevant definitions in: /usr/include/gtk-1.2/gdk/gdktypes.h
+*
+* This routine implements three functions: zoom-to-area, time ruler, and
+* show/hide event detail popup.
+*
+* The left mouse button (button 1) has two simultaneous functions: event
+* detail popup, and zoom-to-area. If the press and release events occur
+* within a small delta-x, it's a detail popup event. Otherwise, it's
+* an area zoom.
+*
+* The right mouse button (button 3) implements the time ruler.
+****************************************************************************/
+
+static gint
+button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ static GdkEventButton press1_event;
+ static boolean press1_valid;
+ static GdkEventButton press3_event;
+ static guint32 last_truler_time;
+ static boolean press3_valid;
+ static boolean zoom_bar_up;
+ int time_ax_y, xdelta;
+ char tmpbuf [128];
+ double time_per_pixel;
+
+ time_ax_y = 0;
+
+ switch(event->type) {
+ case GDK_BUTTON_PRESS:
+ /* Capture the appropriate starting point */
+ if (event->button == 1) {
+ press1_valid = TRUE;
+ press1_event = *event;
+ return(TRUE);
+ }
+ if (event->button == 3) {
+ press3_valid = TRUE;
+ press3_event = *event;
+ return(TRUE);
+ }
+ return(TRUE);
+
+ case GDK_BUTTON_RELEASE:
+ /* Time ruler */
+ if (press3_valid) {
+ press3_valid = FALSE;
+ /* Fix the cursor, and repaint the screen from scratch */
+ gdk_window_set_cursor (da->window, norm_cursor);
+ view1_display_when_idle();
+ return(TRUE);
+ }
+ /* Event select / zoom-to-area */
+ if (press1_valid) {
+ press1_valid = FALSE;
+ xdelta = (int)(press1_event.x - event->x);
+ if (xdelta < 0)
+ xdelta = -xdelta;
+
+ /* is the mouse more or less where it started? */
+ if (xdelta < 10) {
+ /* Control-left-mouse => sink the track */
+ /* Shift-left-mouse => raise the track */
+ if ((press1_event.state & GDK_CONTROL_MASK) ==
+ GDK_CONTROL_MASK) {
+ move_current_track(event, s_v1, MOVE_BOTTOM);
+ } else if ((press1_event.state & GDK_SHIFT_MASK) ==
+ GDK_SHIFT_MASK) {
+ move_current_track(event, s_v1, MOVE_TOP);
+ } else {
+ /* No modifiers: toggle the event */
+ toggle_event_select(event, s_v1);
+ }
+ /* Repaint to get rid of the zoom bar */
+ if (zoom_bar_up) {
+ /* Fix the cursor and leave. No zoom */
+ gdk_window_set_cursor (da->window, norm_cursor);
+ zoom_bar_up = FALSE;
+ break;
+ }
+ } else { /* mouse moved enough to zoom */
+ zoom_event(&press1_event, event, s_v1);
+ gdk_window_set_cursor (da->window, norm_cursor);
+ zoom_bar_up = FALSE;
+ }
+ } else if (event->button == 4) {
+ /* scroll wheel up */
+ scroll_y(event->state & GDK_SHIFT_MASK ? -10 : -1);
+ } else if (event->button == 5) {
+ /* scroll wheel down */
+ scroll_y(event->state & GDK_SHIFT_MASK ? +10 : +1);
+ }
+ return(TRUE);
+
+ case GDK_MOTION_NOTIFY:
+ /* Button one followed by motion: draw zoom fence and fix cursor */
+ if (press1_valid) {
+ /* Fence, cursor already set */
+ if (zoom_bar_up)
+ return(TRUE);
+
+ xdelta = (int)(press1_event.x - event->x);
+ if (xdelta < 0)
+ xdelta = -xdelta;
+
+ /* Haven't moved enough to declare a zoom sequence yet */
+ if (xdelta < 10)
+ return(TRUE);
+
+ /* Draw the zoom fence, use the key-down X coordinate */
+ time_ax_y = s_v1->npids * s_v1->strip_height + s_v1->pid_ax_offset;
+
+ line((int)(press1_event.x), s_v1->pop_offset,
+ (int)(press1_event.x), time_ax_y, LINE_DRAW_BLACK);
+ tbox("Zoom From Here...", (int)(press1_event.x), s_v1->pop_offset,
+ TBOX_DRAW_BOXED);
+ gdk_window_set_cursor(da->window, zi_cursor);
+ zoom_bar_up = TRUE;
+ return(TRUE);
+ }
+ if (press3_valid) {
+ double nsec;
+
+ gdk_window_set_cursor(da->window, zi_cursor);
+
+ /*
+ * Some filtration is needed on Solaris, or the server will hang
+ */
+ if (event->time - last_truler_time < 75)
+ return(TRUE);
+
+ last_truler_time = event->time;
+
+ line((int)(press3_event.x), s_v1->pop_offset,
+ (int)(press3_event.x), time_ax_y, LINE_DRAW_BLACK);
+
+ xdelta = (int)(press3_event.x - event->x);
+ if (xdelta < 0)
+ xdelta = -xdelta;
+
+ time_per_pixel = ((double)(s_v1->maxvistime - s_v1->minvistime)) /
+ ((double)(s_v1->total_width - s_v1->pid_ax_width));
+
+ time_ax_y = s_v1->npids * s_v1->strip_height + s_v1->pid_ax_offset;
+
+ line((int)(press3_event.x), s_v1->pop_offset,
+ (int)(press3_event.x), time_ax_y, LINE_DRAW_BLACK);
+ /*
+ * Note: use a fixed-width format so it looks like we're
+ * erasing and redrawing the box.
+ */
+ nsec = ((double)xdelta)*time_per_pixel;
+ if (nsec >1e9) {
+ sprintf(tmpbuf, "%8.3f sec ", nsec/1e9);
+ } else if (nsec > 1e6) {
+ sprintf(tmpbuf, "%8.3f msec", nsec/1e6);
+ } else if (nsec > 1e3) {
+ sprintf(tmpbuf, "%8.3f usec", nsec/1e3);
+ } else {
+ sprintf(tmpbuf, "%8.0f nsec", nsec);
+ }
+ tbox(tmpbuf, (int)(press3_event.x), s_v1->pop_offset,
+ TBOX_DRAW_BOXED);
+ return(TRUE);
+ }
+
+ default:
+ break;
+#ifdef DEBUG
+ g_print("button:\ttype = %d\n", event->type);
+ g_print("\twindow = 0x%x\n", event->window);
+ g_print("\tsend_event = %d\n", event->send_event);
+ g_print("\ttime = %d\n", event->time);
+ g_print("\tx = %6.2f\n", event->x);
+ g_print("\ty = %6.2f\n", event->y);
+ g_print("\tpressure = %6.2f\n", event->pressure);
+ g_print("\txtilt = %6.2f\n", event->xtilt);
+ g_print("\tytilt = %6.2f\n", event->ytilt);
+ g_print("\tstate = %d\n", event->state);
+ g_print("\tbutton = %d\n", event->button);
+ g_print("\tsource = %d\n", event->source);
+ g_print("\tdeviceid = %d\n", event->deviceid);
+ g_print("\tx_root = %6.2f\n", event->x_root);
+ g_print("\ty_root = %6.2f\n", event->y_root);
+ return(TRUE);
+#endif
+ }
+
+ view1_display_when_idle();
+
+ return(TRUE);
+}
+
+/****************************************************************************
+* configure_event
+* Happens when the window manager resizes the viewer's main window.
+****************************************************************************/
+
+static gint
+configure_event (GtkWidget *widget, GdkEventConfigure *event)
+{
+ /* Toss the previous drawing area backing store pixmap */
+ if (pm)
+ gdk_pixmap_unref(pm);
+
+ /* Create a new pixmap, paint it */
+ pm = gdk_pixmap_new(widget->window,
+ widget->allocation.width,
+ widget->allocation.height,
+ -1);
+ gdk_draw_rectangle (pm,
+ widget->style->white_gc,
+ TRUE,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ /* Reset the view geometry parameters, as required */
+ s_v1->total_width = widget->allocation.width;
+ s_v1->total_height = widget->allocation.height;
+ s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
+ s_v1->strip_height;
+
+ /* Schedule a repaint */
+ view1_display_when_idle();
+ return(TRUE);
+}
+
+/****************************************************************************
+* expose_event
+* Use backing store to fix the screen.
+****************************************************************************/
+static gint expose_event (GtkWidget *widget, GdkEventExpose *event)
+{
+ gdk_draw_pixmap(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ pm,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ return(FALSE);
+}
+
+/****************************************************************************
+* event_search_internal
+* This routine searches forward from s_srchindex, looking for s_srchcode;
+* wraps at the end of the buffer.
+****************************************************************************/
+
+boolean event_search_internal (void)
+{
+ event_t *ep;
+ int i;
+ int index;
+ int pid_index;
+ boolean full_redisplay = FALSE;
+ ulonglong current_width;
+ char tmpbuf [64];
+
+ /* No events yet? Act like the search worked, to avoid a loop */
+ if (g_nevents == 0)
+ return(TRUE);
+
+ ep = (g_events + s_srchindex);
+ ep->flags &= ~EVENT_FLAG_SEARCHRSLT;
+
+ /*
+ * Assume the user wants to search [plus or minus]
+ * from where they are.
+ */
+#ifdef notdef
+ if (ep->time < s_v1->minvistime)
+ s_srchindex = find_event_index (s_v1->minvistime);
+#endif
+
+ for (i = 1; i <= g_nevents; i++) {
+ index = (srch_chase_dir == SRCH_CHASE_BACKWARD) ?
+ (s_srchindex - i) % g_nevents :
+ (i + s_srchindex) % g_nevents;
+
+ ep = (g_events + index);
+
+ if (ep->code == s_srchcode) {
+ if (s_srchfail_up)
+ message_line("");
+ s_srchindex = index;
+ pid_index = ep->pid->pid_index;
+
+ /* Need a vertical scroll? */
+ if ((pid_index < s_v1->first_pid_index) ||
+ (pid_index >= s_v1->first_pid_index + s_v1->npids)) {
+ if (pid_index > (g_npids - s_v1->npids))
+ pid_index = (g_npids - s_v1->npids);
+ s_v1->first_pid_index = pid_index;
+ GTK_ADJUSTMENT(s_view1_vsadj)->value =
+ (gdouble)s_v1->first_pid_index;
+ gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
+ full_redisplay = TRUE;
+ }
+
+ /* Need a horizontal scroll? */
+ if (ep->time < s_v1->minvistime || ep->time > s_v1->maxvistime) {
+ current_width = (s_v1->maxvistime - s_v1->minvistime);
+ if (ep->time < ((current_width+1) / 2)) {
+ s_v1->minvistime = 0ll;
+ s_v1->maxvistime = current_width;
+ } else {
+ s_v1->minvistime = ep->time - ((current_width+1)/2);
+ s_v1->maxvistime = ep->time + ((current_width+1)/2);
+ }
+ recompute_hscrollbar();
+ full_redisplay = TRUE;
+ }
+ ep->flags |= EVENT_FLAG_SEARCHRSLT;
+ full_redisplay = TRUE;
+
+#ifdef NOTDEF
+ if (!full_redisplay){
+ if (!s_result_up) {
+ s_result_up = TRUE;
+ time_per_pixel = dtime_per_pixel(s_v1);
+
+ y = pid_index*s_v1->strip_height + s_v1->event_offset;
+ x = s_v1->pid_ax_width +
+ (int)(((double)(ep->time - s_v1->minvistime)) /
+ time_per_pixel);
+ sprintf(tmpbuf, "SEARCH RESULT");
+ tbox(tmpbuf, x, y - s_v1->pop_offset, TBOX_DRAW_BOXED);
+ line(x, y-s_v1->pop_offset, x, y, LINE_DRAW_BLACK);
+ } else {
+ full_redisplay = TRUE;
+ }
+ }
+#endif
+
+ if (full_redisplay)
+ view1_display_when_idle();
+ return(TRUE);
+ }
+ }
+ sprintf (tmpbuf, "Search for event %ld failed...\n", s_srchcode);
+ message_line(tmpbuf);
+ s_srchfail_up = TRUE;
+ return(TRUE);
+}
+
+/****************************************************************************
+* event_search_callback
+****************************************************************************/
+
+boolean event_search_callback (char *s)
+{
+ /* No events yet? Act like the search worked, to avoid a loop */
+ if (g_nevents == 0)
+ return(TRUE);
+
+ s_srchcode = atol(s);
+
+ if (s_srchcode == 0)
+ return(FALSE);
+
+ return(event_search_internal());
+}
+
+/****************************************************************************
+* event_search
+****************************************************************************/
+
+static void event_search (void)
+{
+ modal_dialog ("Event Search: Please Enter Event Code",
+ "Invalid: Please Reenter Event Code", NULL,
+ event_search_callback);
+}
+
+/****************************************************************************
+* init_track_colors
+****************************************************************************/
+static void init_track_colors(void)
+{
+ int i;
+ unsigned hash;
+ char *label_char;
+ unsigned RGB[3];
+ gboolean dont_care[g_npids];
+
+ /*
+ * If we've already allocated the colors once, then in theory we should
+ * just be able to re-order the GCs already created to match the new track
+ * order; the track -> color mapping doesn't currently change at runtime.
+ * However, it's easier just to allocate everything from fresh. As a nod in
+ * the direction of politeness towards our poor abused X server, we at
+ * least mop up the previously allocated GCs first, although in practice
+ * even omitting this didn't seem to cause a problem.
+ */
+ if (s_color != NULL ) {
+ gdk_colormap_free_colors(gtk_widget_get_colormap(da),
+ s_color, g_npids);
+ memset(s_color, 0, sizeof(GdkColor) * g_npids);
+ } else {
+ /*
+ * First time through: allocate the array to hold the GCs.
+ */
+ s_color = g_malloc(sizeof(GdkColor) * g_npids);
+ }
+
+ /*
+ * Go through and assign a color for each track.
+ */
+ for (i = 0; i < g_npids; i++) {
+ /*
+ * We compute the color from a hash of the thread name. That way we get
+ * a distribution of different colors, and the same thread has the same
+ * color across multiple data sets. Unfortunately, even though the
+ * process name and thread id are invariant across data sets, the
+ * process id isn't, so we want to exclude that from the hash. Since
+ * the pid appears in parentheses after the process name and tid, we
+ * can just stop at the '(' character.
+ *
+ * We could create a substring and use the CLIB Jenkins hash, but given
+ * we're hashing ascii data, a suitable Bernstein hash is pretty much
+ * just as good, and it's easiest just to compute it inline.
+ */
+ label_char = get_track_label(g_pids[i].pid_value);
+ hash = 0;
+ while (*label_char != '\0' && *label_char != '(') {
+ hash = hash * 33 + *label_char++;
+ }
+ hash += hash >> 5; /* even out the lower order bits a touch */
+
+ /*
+ * OK, now we have our hash. We get the color by using the first three
+ * bytes of the hash for the RGB values (expanded from 8 to 16 bits),
+ * and then use the fourth byte to choose one of R, G, B and mask this
+ * one down. This ensures the color can't be too close to white and
+ * therefore hard to see.
+ *
+ * We also drop the top bit of the green, since bright green on its own
+ * is hard to see against white. Generally we err on the side of
+ * keeping it dark, rather than using the full spectrum of colors. This
+ * does result in something of a preponderance of muddy colors and a
+ * bit of a lack of cheery bright ones, but at least you can read
+ * everything. It would be nice to do better.
+ */
+ RGB[0] = (hash & 0xff000000) >> 16;
+ RGB[1] = (hash & 0x007f0000) >> 8;
+ RGB[2] = (hash & 0x0000ff00);
+ RGB[hash % 3] &= 0x1fff;
+
+ {
+ GdkColor color = {0, RGB[0], RGB[1], RGB[2]};
+ s_color[i] = color;
+ g_pids[i].color_index = i;
+ }
+ }
+
+ /*
+ * Actually allocate the colors in one bulk operation. We ignore the return
+ * values.
+ */
+ gdk_colormap_alloc_colors(gtk_widget_get_colormap(da),
+ s_color, g_npids, FALSE, TRUE, dont_care);
+}
+
+
+/****************************************************************************
+* chase_event_etc
+* Reorder the pid_index fields so the viewer "chases" the last selected
+* event.
+****************************************************************************/
+
+static void chase_event_etc(enum chase_mode mode)
+{
+ pid_sort_t *psp, *new_pidvec;
+ pid_data_t *pp;
+ event_t *ep;
+ int pids_mapped;
+ ulong code_to_chase;
+ ulong datum_to_chase;
+ ulong pid_to_chase;
+ int i;
+ int winner;
+
+ if (!s_last_selected_event) {
+ infobox("No selected event",
+ "\nPlease select an event and try again...\n");
+ return;
+ }
+
+ /* Clear all index assignments */
+ psp = g_pids;
+ for (i = 0; i < g_npids; i++) {
+ pp = psp->pid;
+ pp->pid_index = 0xFFFFFFFF;
+ psp++;
+ }
+
+ ep = s_last_selected_event;
+ code_to_chase = ep->code;
+ datum_to_chase = ep->datum;
+ pid_to_chase = ep->pid->pid_value;
+ pids_mapped = 0;
+ new_pidvec = g_malloc(sizeof(pid_sort_t)*g_npids);
+
+ while (1) {
+ if (srch_chase_dir == SRCH_CHASE_FORWARD) {
+ if (ep >= g_events + g_nevents)
+ break;
+ } else {
+ if (ep < g_events)
+ break;
+ }
+
+ winner = 0;
+ switch(mode) {
+ case CHASE_EVENT:
+ if (ep->code == code_to_chase) {
+ winner = 1;
+ }
+ break;
+
+ case CHASE_DATUM:
+ if (ep->datum == datum_to_chase) {
+ winner = 1;
+ }
+ break;
+
+ case CHASE_TRACK:
+ if (ep->pid->pid_value == pid_to_chase) {
+ winner = 1;
+ }
+ break;
+
+ default:
+ infobox("BUG", "unknown mode in chase_event_etc\n");
+ break;
+ }
+
+ if (winner) {
+ if (ep->pid->pid_index == 0xFFFFFFFF) {
+ ep->pid->pid_index = pids_mapped;
+ new_pidvec[pids_mapped].pid = ep->pid;
+ new_pidvec[pids_mapped].pid_value = ep->pid->pid_value;
+ new_pidvec[pids_mapped].color_index = 0;
+ pids_mapped++;
+ if (pids_mapped == g_npids)
+ break;
+ }
+ }
+ if (srch_chase_dir == SRCH_CHASE_FORWARD)
+ ep++;
+ else
+ ep--;
+ }
+
+ /* Pass 2, first-to-last, to collect stragglers */
+ ep = g_events;
+
+ while (ep < g_events + g_nevents) {
+ if (ep->pid->pid_index == 0xFFFFFFFF) {
+ ep->pid->pid_index = pids_mapped;
+ new_pidvec[pids_mapped].pid = ep->pid;
+ new_pidvec[pids_mapped].pid_value = ep->pid->pid_value;
+ new_pidvec[pids_mapped].color_index = 0;
+ pids_mapped++;
+ if (pids_mapped == g_npids)
+ break;
+ }
+ ep++;
+ }
+
+ if (pids_mapped != g_npids) {
+ infobox("BUG", "\nDidn't map all pids in chase_event_etc\n");
+ }
+
+ g_free (g_pids);
+ g_pids = new_pidvec;
+
+ /*
+ * The new g_pids vector contains the "chase" sort, so we revert
+ * the pid_index mapping to an identity map
+ */
+ psp = g_pids;
+
+ for (i = 0; i < g_npids; i++) {
+ pp = psp->pid;
+ pp->pid_index = i;
+ psp++;
+ }
+
+ /* AutoScroll the PID axis so we show the first "chased" event */
+ s_v1->first_pid_index = 0;
+ GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00;
+ gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
+ init_track_colors();
+ view1_display_when_idle();
+}
+
+/****************************************************************************
+* unchase_event_etc
+* Copy g_original_pids to g_pids, revert index mapping
+****************************************************************************/
+static void unchase_event_etc(void)
+{
+ int i;
+ pid_sort_t *psp;
+ pid_data_t *pp;
+
+ memcpy (g_pids, g_original_pids, sizeof(pid_sort_t)*g_npids);
+
+ /* Fix the pid structure index mappings */
+ psp = g_pids;
+
+ for (i = 0; i < g_npids; i++) {
+ pp = psp->pid;
+ pp->pid_index = i;
+ psp++;
+ }
+
+ /* Scroll PID axis to the top */
+ s_v1->first_pid_index = 0;
+ GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00;
+ gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
+ init_track_colors();
+ view1_display_when_idle();
+}
+
+/****************************************************************************
+* print_ps_header
+* To fit a reasonable-sized landscape mode plot onto letter-size paper,
+* scale everything by .75.
+****************************************************************************/
+
+static void print_ps_header (v1_geometry_t *vp, char *filename)
+{
+ time_t now;
+
+ now = time(0);
+
+ fprintf(s_printfp, "%%%%!PS-Adobe-3.0 EPSF-3.0\n");
+ fprintf(s_printfp, "%%%%Creator: G2 Event Viewer\n");
+ fprintf(s_printfp, "%%%%Title: %s\n", filename);
+ fprintf(s_printfp, "%%%%CreationDate: %s", ctime(&now));
+ fprintf(s_printfp, "%%%%DocumentData: Clean7Bit\n");
+ fprintf(s_printfp, "%%%%Origin: 0 0\n");
+ fprintf(s_printfp, "%%%%BoundingBox: 0 0 %d %d\n", vp->total_height,
+ vp->total_width);
+ fprintf(s_printfp, "%%%%LanguageLevel: 2\n");
+ fprintf(s_printfp, "%%%%Pages: 1\n");
+ fprintf(s_printfp, "%%%%Page: 1 1\n");
+ fprintf(s_printfp, "%%%%EOF\n");
+ fprintf(s_printfp, "/Times-Roman findfont\n");
+ fprintf(s_printfp, "12 scalefont\n");
+ fprintf(s_printfp, "setfont\n");
+ fprintf(s_printfp, ".75 .75 scale\n");
+}
+
+/****************************************************************************
+* xrt
+* Xcoordinate rotate and translate. We need to emit postscript that
+* has a reasonable aspect ratio for printing. To do that, we rotate the
+* intended picture by 90 degrees, using the standard 2D rotation
+* formula:
+*
+* Xr = x*cos(theta) - y*sin(theta);
+* Yr = x*sin(theta) + y*cos(theta);
+*
+* If we let theta = 90, this reduces to
+* Xr = -y
+* Yr = x
+*
+* Translate back to the origin in X by adding Ymax, yielding
+* Xrt = Ymax - y
+****************************************************************************/
+
+static inline int xrt(int x, int y)
+{
+ return (s_v1->total_height - y);
+}
+
+static inline int yrt(int x, int y)
+{
+ return(x);
+}
+
+/****************************************************************************
+* print_screen_callback
+****************************************************************************/
+
+static boolean print_screen_callback(char *filename)
+{
+ s_printfp = fopen (filename, "wt");
+
+ if (s_printfp == NULL)
+ return(FALSE);
+
+ /*
+ * This variable allows us to magically turn the view1 display
+ * code into a print-driver, with a minimum of fuss. The idea is to
+ * magically change TBOX_DRAW_XXX into TBOX_PRINT_XXX by adding
+ * the required value, aka s_print_offset.
+ * Make sure to fix g2.h if you mess here, or vice versa.
+ */
+ s_print_offset = TBOX_PRINT_PLAIN - TBOX_DRAW_PLAIN;
+
+ print_ps_header(s_v1, filename);
+
+ display_pid_axis(s_v1);
+ display_event_data(s_v1);
+ display_time_axis(s_v1);
+
+ fclose (s_printfp);
+ s_printfp = 0;
+ s_print_offset = 0;
+
+ /* For tactile feedback */
+ view1_display_when_idle();
+ return(TRUE);
+}
+
+/****************************************************************************
+* view1_button_click_callback
+****************************************************************************/
+
+static void view1_button_click_callback(GtkButton *item, gpointer data)
+{
+ enum view1_button_click click = (enum view1_button_click) data;
+ event_t *ep;
+ ulonglong event_incdec;
+ ulonglong current_width;
+ ulonglong zoom_delta;
+
+
+ current_width = s_v1->maxvistime - s_v1->minvistime;
+ event_incdec = (current_width) / 3;
+
+ if (event_incdec == 0LL)
+ event_incdec = 1;
+
+ zoom_delta = (s_v1->maxvistime - s_v1->minvistime) / 6;
+
+ switch(click) {
+ case TOP_BUTTON:
+ /* First PID to top of window */
+ s_v1->first_pid_index = 0;
+ GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00;
+ gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
+ break;
+
+ case BOTTOM_BUTTON:
+ s_v1->first_pid_index = g_npids - s_v1->npids;
+ if (s_v1->first_pid_index < 0)
+ s_v1->first_pid_index = 0;
+ GTK_ADJUSTMENT(s_view1_vsadj)->value = (gdouble)s_v1->first_pid_index;
+ gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj));
+ break;
+
+ case SNAP_BUTTON:
+ add_snapshot();
+ break;
+
+ case NEXT_BUTTON:
+ next_snapshot();
+ break;
+
+ case DEL_BUTTON:
+ del_snapshot();
+ break;
+
+ case CHASE_EVENT_BUTTON:
+ chase_event_etc(CHASE_EVENT);
+ break;
+
+ case CHASE_DATUM_BUTTON:
+ chase_event_etc(CHASE_DATUM);
+ break;
+
+ case CHASE_TRACK_BUTTON:
+ chase_event_etc(CHASE_TRACK);
+ break;
+
+ case UNCHASE_BUTTON:
+ unchase_event_etc();
+ break;
+
+ case START_BUTTON:
+ start_button:
+ s_v1->minvistime = 0LL;
+ s_v1->maxvistime = current_width;
+ recompute_hscrollbar();
+ break;
+
+ case ZOOMIN_BUTTON:
+ s_v1->minvistime += zoom_delta;
+ s_v1->maxvistime -= zoom_delta;
+ recompute_hscrollbar();
+ break;
+
+ case SEARCH_AGAIN_BUTTON:
+ if (s_srchcode) {
+ event_search_internal();
+ break;
+ }
+ /* NOTE FALLTHROUGH */
+
+ case SEARCH_BUTTON:
+ event_search();
+ break;
+
+ case ZOOMOUT_BUTTON:
+ if (zoom_delta == 0LL)
+ zoom_delta = 1;
+
+ if (s_v1->minvistime >= zoom_delta) {
+ s_v1->minvistime -= zoom_delta;
+ s_v1->maxvistime += zoom_delta;
+ } else {
+ s_v1->minvistime = 0;
+ s_v1->maxvistime += zoom_delta*2;
+ }
+
+ if ((s_v1->maxvistime - s_v1->minvistime) * 8 >
+ g_events[g_nevents-1].time * 9) {
+ s_v1->minvistime = 0;
+ s_v1->maxvistime = g_events[g_nevents-1].time * 9 / 8;
+ }
+ recompute_hscrollbar();
+ break;
+
+ case END_BUTTON:
+ ep = (g_events + g_nevents - 1);
+ s_v1->maxvistime = ep->time + event_incdec/3;
+ s_v1->minvistime = s_v1->maxvistime - current_width;
+ if (s_v1->minvistime > s_v1->maxvistime)
+ goto start_button;
+ recompute_hscrollbar();
+ break;
+
+ case MORE_TRACES_BUTTON:
+ /* Reduce the strip height to fit more traces on screen */
+ s_v1->strip_height -= 1;
+
+ if (s_v1->strip_height < 1) {
+ s_v1->strip_height = 1;
+ }
+
+ /* Recalculate the number of strips on the screen */
+ s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
+ s_v1->strip_height;
+ recompute_vscrollbar();
+ break;
+
+ case LESS_TRACES_BUTTON:
+ /* Increase the strip height to fit fewer on the screen */
+ s_v1->strip_height += 1;
+ if (s_v1->strip_height > 80) {
+ s_v1->strip_height = 80;
+ }
+
+ /* Recalculate the number of strips on the screen */
+ s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
+ s_v1->strip_height;
+ recompute_vscrollbar();
+ break;
+
+ case FORWARD_BUTTON:
+ srch_chase_dir = SRCH_CHASE_FORWARD;
+ gtk_widget_hide (s_view1_forward_button);
+ gtk_widget_show (s_view1_backward_button);
+ break;
+
+ case BACKWARD_BUTTON:
+ srch_chase_dir = SRCH_CHASE_BACKWARD;
+ gtk_widget_show (s_view1_forward_button);
+ gtk_widget_hide (s_view1_backward_button);
+ break;
+
+ case SUMMARY_BUTTON:
+ summary_mode = TRUE;
+ gtk_widget_hide (s_view1_summary_button);
+ gtk_widget_show (s_view1_nosummary_button);
+ break;
+
+ case NOSUMMARY_BUTTON:
+ summary_mode = FALSE;
+ gtk_widget_show (s_view1_summary_button);
+ gtk_widget_hide (s_view1_nosummary_button);
+ break;
+ }
+
+ view1_display_when_idle();
+}
+
+/****************************************************************************
+* view1_print_callback
+****************************************************************************/
+
+void view1_print_callback (GtkToggleButton *notused, gpointer nu2)
+{
+ modal_dialog("Print Screen (PostScript format) to file:",
+ "Invalid file: Print Screen to file:",
+ "g2.ps", print_screen_callback);
+}
+
+/****************************************************************************
+* view1_hscroll
+****************************************************************************/
+
+static void view1_hscroll (GtkAdjustment *adj, GtkWidget *notused)
+{
+ ulonglong current_width;
+
+ current_width = (s_v1->maxvistime - s_v1->minvistime);
+
+ s_v1->minvistime = (ulonglong)(adj->value);
+ s_v1->maxvistime = s_v1->minvistime + current_width;
+
+ view1_display_when_idle();
+
+#ifdef NOTDEF
+ g_print ("adj->lower = %.2f\n", adj->lower);
+ g_print ("adj->upper = %.2f\n", adj->upper);
+ g_print ("adj->value = %.2f\n", adj->value);
+ g_print ("adj->step_increment = %.2f\n", adj->step_increment);
+ g_print ("adj->page_increment = %.2f\n", adj->page_increment);
+ g_print ("adj->page_size = %.2f\n", adj->page_size);
+#endif
+}
+
+/****************************************************************************
+* view1_vscroll
+****************************************************************************/
+
+static void view1_vscroll (GtkAdjustment *adj, GtkWidget *notused)
+{
+ s_v1->first_pid_index = (int)adj->value;
+ view1_display_when_idle();
+}
+
+void set_pid_ax_width(int width)
+{
+ s_v1->pid_ax_width = width;
+ view1_display_when_idle();
+}
+
+/****************************************************************************
+* view1_init
+****************************************************************************/
+
+void view1_init(void)
+{
+
+ c_view1_draw_width = atol(getprop_default("drawbox_width", "700"));
+ c_view1_draw_height = atol(getprop_default("drawbox_height", "400"));
+
+ s_v1->pid_ax_width = 80;
+ s_v1->time_ax_height = 80;
+ s_v1->time_ax_spacing = 100;
+ s_v1->strip_height = 25;
+ s_v1->pop_offset = 20;
+ s_v1->pid_ax_offset = 34;
+ s_v1->event_offset = 40;
+ s_v1->total_height = c_view1_draw_height;
+ s_v1->total_width = c_view1_draw_width;
+ s_v1->first_pid_index = 0;
+
+ s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) /
+ s_v1->strip_height;
+
+ s_v1->minvistime = 0;
+ s_v1->maxvistime = 200;
+
+ s_view1_vbox = gtk_vbox_new(FALSE, 5);
+
+ s_view1_hbox = gtk_hbox_new(FALSE, 5);
+
+ da = gtk_drawing_area_new();
+ gtk_drawing_area_size(GTK_DRAWING_AREA(da), c_view1_draw_width,
+ c_view1_draw_height);
+
+#ifdef NOTDEF
+ gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event",
+ (GtkSignalFunc) motion_notify_event, NULL);
+#endif
+
+ gtk_signal_connect (GTK_OBJECT (da), "expose_event",
+ (GtkSignalFunc) expose_event, NULL);
+
+ gtk_signal_connect (GTK_OBJECT(da),"configure_event",
+ (GtkSignalFunc) configure_event, NULL);
+
+ gtk_signal_connect (GTK_OBJECT (da), "button_press_event",
+ (GtkSignalFunc) button_press_event, NULL);
+
+ gtk_signal_connect (GTK_OBJECT (da), "button_release_event",
+ (GtkSignalFunc) button_press_event, NULL);
+
+ gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event",
+ (GtkSignalFunc) button_press_event, NULL);
+
+ gtk_widget_set_events (da, GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK
+ | GDK_BUTTON_MOTION_MASK);
+
+
+ gtk_box_pack_start(GTK_BOX(s_view1_hbox), da, TRUE, TRUE, 0);
+
+ g_font = gdk_font_load ("8x13");
+ if (g_font == NULL) {
+ g_error("Couldn't load 8x13 font...\n");
+ }
+ gdk_font_ref(g_font);
+
+ /* PID axis menu */
+ s_view1_vmenubox = gtk_vbox_new(FALSE, 5);
+
+ s_view1_vsadj = gtk_adjustment_new(0.0 /* initial value */,
+ 0.0 /* minimum value */,
+ 2000.0 /* maximum value */,
+ 0.1 /* step increment */,
+ 10.0/* page increment */,
+ 10.0/* page size */);
+
+ s_view1_vscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT(s_view1_vsadj));
+
+ gtk_signal_connect (GTK_OBJECT (s_view1_vsadj), "value-changed",
+ GTK_SIGNAL_FUNC (view1_vscroll),
+ (gpointer)s_view1_vscroll);
+
+ s_view1_topbutton = gtk_button_new_with_label("Top");
+ s_view1_bottombutton = gtk_button_new_with_label("Bottom");
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_topbutton), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) TOP_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_bottombutton), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) BOTTOM_BUTTON);
+
+ /* More Traces button and Less Traces button */
+ s_view1_more_traces_button = gtk_button_new_with_label("More Traces");
+ s_view1_less_traces_button = gtk_button_new_with_label("Less Traces");
+ gtk_signal_connect (GTK_OBJECT(s_view1_more_traces_button), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) MORE_TRACES_BUTTON);
+ gtk_signal_connect (GTK_OBJECT(s_view1_less_traces_button), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) LESS_TRACES_BUTTON);
+
+#ifdef NOTDEF
+ /* Trick to bottom-justify the menu: */
+ s_view1_pad1 = gtk_vbox_new(FALSE, 0);
+ gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_pad1,
+ TRUE, FALSE, 0);
+
+#endif
+
+ gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_topbutton,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_vscroll,
+ TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_bottombutton,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_more_traces_button,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_less_traces_button,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hbox), s_view1_vmenubox,
+ FALSE, FALSE, 0);
+
+ /* Time axis menu */
+
+ s_view1_hmenubox = gtk_hbox_new(FALSE, 5);
+
+ s_view1_startbutton = gtk_button_new_with_label("Start");
+
+ s_view1_zoominbutton = gtk_button_new_with_label("ZoomIn");
+
+ s_view1_searchbutton = gtk_button_new_with_label("Search");
+
+ s_view1_srchagainbutton = gtk_button_new_with_label("Search Again");
+
+ s_view1_zoomoutbutton = gtk_button_new_with_label("ZoomOut");
+
+ s_view1_endbutton = gtk_button_new_with_label("End");
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_startbutton), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) START_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_zoominbutton), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) ZOOMIN_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_searchbutton), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) SEARCH_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_srchagainbutton), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) SEARCH_AGAIN_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_zoomoutbutton), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) ZOOMOUT_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_endbutton), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) END_BUTTON);
+
+ s_view1_hsadj = gtk_adjustment_new(0.0 /* initial value */,
+ 0.0 /* minimum value */,
+ 2000.0 /* maximum value */,
+ 0.1 /* step increment */,
+ 10.0/* page increment */,
+ 10.0/* page size */);
+
+ s_view1_hscroll = gtk_hscrollbar_new (GTK_ADJUSTMENT(s_view1_hsadj));
+
+ gtk_signal_connect (GTK_OBJECT (s_view1_hsadj), "value-changed",
+ GTK_SIGNAL_FUNC (view1_hscroll),
+ (gpointer)s_view1_hscroll);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_startbutton,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_hscroll,
+ TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_endbutton,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_zoominbutton,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_searchbutton,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_srchagainbutton,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_zoomoutbutton,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hbox,
+ TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hmenubox,
+ FALSE, FALSE, 0);
+
+
+ s_view1_hmenubox2 = gtk_hbox_new(FALSE, 5);
+
+ s_view1_snapbutton = gtk_button_new_with_label("Snap");
+
+ s_view1_nextbutton = gtk_button_new_with_label("Next");
+
+ s_view1_delbutton = gtk_button_new_with_label("Del");
+
+ s_view1_chase_event_button = gtk_button_new_with_label("ChaseEvent");
+
+ s_view1_chase_datum_button = gtk_button_new_with_label("ChaseDatum");
+
+ s_view1_chase_track_button = gtk_button_new_with_label("ChaseTrack");
+
+ s_view1_unchasebutton = gtk_button_new_with_label("NoChase");
+
+ s_view1_forward_button = gtk_button_new_with_label("->SrchChase(is<-)");
+ s_view1_backward_button = gtk_button_new_with_label("<-SrchChase(is->)");
+
+ s_view1_summary_button = gtk_button_new_with_label("Summary");
+ s_view1_nosummary_button = gtk_button_new_with_label("NoSummary");
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_snapbutton), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) SNAP_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_nextbutton), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) NEXT_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_delbutton), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) DEL_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_chase_event_button), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) CHASE_EVENT_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_chase_datum_button), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) CHASE_DATUM_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_chase_track_button), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) CHASE_TRACK_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_unchasebutton), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) UNCHASE_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_forward_button), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) FORWARD_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_backward_button), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) BACKWARD_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_summary_button), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) SUMMARY_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_nosummary_button), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) NOSUMMARY_BUTTON);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hmenubox2,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_snapbutton,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_nextbutton,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_delbutton,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_event_button,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_datum_button,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_track_button,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_unchasebutton,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_forward_button,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_backward_button,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_summary_button,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_nosummary_button,
+ FALSE, FALSE, 0);
+
+ s_view1_label = gtk_label_new(NULL);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_label,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(g_mainhbox), s_view1_vbox,
+ TRUE, TRUE, 0);
+
+ gtk_widget_show_all (s_view1_vbox);
+ GTK_WIDGET_SET_FLAGS(da, GTK_CAN_FOCUS);
+ gtk_widget_grab_focus(da);
+
+ gtk_widget_hide (s_view1_forward_button);
+ gtk_widget_hide (summary_mode ? s_view1_summary_button
+ : s_view1_nosummary_button);
+
+ zi_source = gdk_bitmap_create_from_data (NULL, (char *)zi_bits, zi_width,
+ zi_height);
+ zi_mask = gdk_bitmap_create_from_data (NULL, (char *)zi_bkgd, zi_width,
+ zi_height);
+
+ zi_cursor = (GdkCursor *) gdk_cursor_new_from_pixmap (zi_source,
+ zi_mask, &fg_black,
+ &bg_white, zi_x_hot,
+ zi_y_hot);
+ gdk_pixmap_unref (zi_source);
+ gdk_pixmap_unref (zi_mask);
+
+ norm_cursor = (GdkCursor *) gdk_cursor_new (GDK_TOP_LEFT_ARROW);
+}
+
+/****************************************************************************
+* line_print
+****************************************************************************/
+
+void line_print (int x1, int y1, int x2, int y2)
+{
+ fprintf(s_printfp, "newpath\n");
+ fprintf(s_printfp, "%d %d moveto\n", xrt(x1, s_v1->total_height - y1),
+ yrt(x1, s_v1->total_height - y1));
+
+ fprintf(s_printfp, "%d %d lineto\n", xrt (x2, s_v1->total_height - y2),
+ yrt (x2, s_v1->total_height - y2));
+ fprintf(s_printfp, "1 setlinewidth\n");
+ fprintf(s_printfp, "stroke\n");
+}
+
+/****************************************************************************
+* tbox_print
+****************************************************************************/
+GdkRectangle *tbox_print (char *s, int x, int y, enum view1_tbox_fn function,
+ GdkRectangle *rp)
+{
+ if (function == TBOX_PRINT_BOXED) {
+ rp->width -= 4;
+ }
+
+ if ((function == TBOX_PRINT_BOXED) ||
+ (function == TBOX_PRINT_EVENT)) {
+
+ fprintf(s_printfp, "newpath\n");
+ fprintf(s_printfp, "0 setlinewidth\n");
+ fprintf(s_printfp, "%d %d moveto\n",
+ xrt(rp->x, s_v1->total_height - rp->y),
+ yrt(rp->x, s_v1->total_height - rp->y));
+
+ fprintf(s_printfp, "%d %d lineto\n",
+ xrt (rp->x+rp->width, s_v1->total_height - rp->y),
+ yrt (rp->x+rp->width, s_v1->total_height - rp->y));
+
+ fprintf(s_printfp, "%d %d lineto\n",
+ xrt(rp->x+rp->width, s_v1->total_height - (rp->y+rp->height)),
+ yrt(rp->x+rp->width, s_v1->total_height - (rp->y+rp->height)));
+
+ fprintf(s_printfp, "%d %d lineto\n",
+ xrt(rp->x, s_v1->total_height - (rp->y+rp->height)),
+ yrt(rp->x, s_v1->total_height - (rp->y+rp->height)));
+
+ fprintf(s_printfp, "%d %d lineto\n",
+ xrt(rp->x, s_v1->total_height - rp->y),
+ yrt(rp->x, s_v1->total_height - rp->y));
+
+ fprintf(s_printfp, "stroke\n");
+ }
+
+ if ((function == TBOX_PRINT_BOXED) ||
+ (function == TBOX_PRINT_PLAIN)) {
+
+ fprintf(s_printfp, "newpath\n");
+ fprintf(s_printfp, "%d %d moveto\n",
+ xrt(x, s_v1->total_height - (y-2)),
+ yrt(x, s_v1->total_height - (y-2)));
+ fprintf(s_printfp, "gsave\n");
+ fprintf(s_printfp, "90 rotate\n");
+ fprintf(s_printfp, "(%s) show\n", s);
+ fprintf(s_printfp, "grestore\n");
+ }
+
+ return(rp);
+}
+
+/****************************************************************************
+* tbox - draws an optionally boxed string whose lower lefthand
+* corner is at (x, y). As usual, Y is backwards.
+****************************************************************************/
+
+GdkRectangle *tbox (char *s, int x, int y, enum view1_tbox_fn function)
+{
+ static GdkRectangle update_rect;
+ gint lbearing, rbearing, width, ascent, descent;
+
+ gdk_string_extents (g_font, s,
+ &lbearing, &rbearing,
+ &width, &ascent, &descent);
+
+ /*
+ * If we have enough room to display full size events, then just
+ * use the BOXED function instead of the EVENT function.
+ */
+ if (s_v1->strip_height > 9) {
+ switch (function) {
+ case TBOX_DRAW_EVENT: function = TBOX_DRAW_BOXED; break;
+ case TBOX_GETRECT_EVENT: function = TBOX_GETRECT_BOXED; break;
+ case TBOX_PRINT_EVENT: function = TBOX_PRINT_BOXED; break;
+ default:
+ break;
+ /* Nothing */
+ }
+ }
+
+ switch (function) {
+ case TBOX_DRAW_BOXED:
+ gdk_draw_rectangle (pm, da->style->white_gc, TRUE,
+ x, y - (ascent+descent+3), width + 2,
+ ascent + descent + 3);
+
+ gdk_draw_rectangle (pm, da->style->black_gc, FALSE,
+ x, y - (ascent+descent+3), width + 2,
+ ascent + descent + 3);
+
+ gdk_draw_string (pm, g_font, da->style->black_gc,
+ x + 1, y - 1, (const gchar *)s);
+ /* NOTE FALLTHROUGH */
+ case TBOX_GETRECT_BOXED:
+ update_rect.x = x;
+ update_rect.y = y -(ascent+descent+3);
+ update_rect.width = width + 3;
+ update_rect.height = ascent + descent + 4;
+ if (function == TBOX_DRAW_BOXED)
+ gtk_widget_draw (da, &update_rect);
+ break;
+
+ case TBOX_DRAW_EVENT:
+ /* We have a small event to draw...no text */
+ gdk_draw_rectangle (pm, da->style->black_gc, FALSE,
+ x, y - 1, 3, 3);
+ /* NOTE FALLTHROUGH */
+ case TBOX_GETRECT_EVENT:
+ update_rect.x = x;
+ update_rect.y = y - 1;
+ update_rect.width = 4;
+ update_rect.height = 4;
+ if (function == TBOX_DRAW_EVENT)
+ gtk_widget_draw (da, &update_rect);
+ break;
+
+
+ case TBOX_DRAW_PLAIN:
+
+ gdk_draw_string (pm, g_font, da->style->black_gc,
+ x + 1, y - 1, (const gchar *)s);
+ /* NOTE FALLTHROUGH */
+ case TBOX_GETRECT_PLAIN:
+ update_rect.x = x;
+ update_rect.y = y -(ascent+descent+1);
+ update_rect.width = width;
+ update_rect.height = ascent + descent;
+ if (function == TBOX_DRAW_PLAIN)
+ gtk_widget_draw (da, &update_rect);
+ break;
+
+ case TBOX_PRINT_BOXED:
+ update_rect.x = x;
+ update_rect.y = y -(ascent+descent+3);
+ update_rect.width = width + 3;
+ update_rect.height = ascent + descent + 4;
+ /* note fallthrough */
+ case TBOX_PRINT_PLAIN:
+ return(tbox_print(s, x, y, function, &update_rect));
+
+ case TBOX_PRINT_EVENT:
+ /* We have a small event box to print...no text */
+ update_rect.x = x;
+ update_rect.y = y - 1;
+ update_rect.width = 4;
+ update_rect.height = 4;
+ return(tbox_print(s, x, y, function, &update_rect));
+ }
+ return(&update_rect);
+}
+
+/****************************************************************************
+* line
+*
+* For lines there is a primitive batching facility, that doesn't update
+* the drawing area until the batch is complete. This is handy for drawing
+* the pid axis and for summary mode.
+*
+* line_batch_mode contains the state for this:
+*
+* BATCH_OFF: no batching, update for every line
+* BATCH_NEW: just entered a batch, so initialize the area to update from
+* scratch
+* BATCH_EXISTING: have drawn at least one line in batch mode, so the update
+* area should only be expanded from now on to include the
+* union of the "rectangular hull" of all lines
+****************************************************************************/
+
+static enum { BATCH_OFF, BATCH_NEW, BATCH_EXISTING } line_batch_mode;
+static int line_batch_count;
+static int line_minx, line_miny, line_maxx, line_maxy;
+
+void line_batch_start (void)
+{
+ line_batch_mode = BATCH_NEW;
+ line_batch_count = 0;
+}
+
+void line_batch_end (void)
+{
+ GdkRectangle update_rect;
+ if (line_batch_count > 0) {
+ update_rect.x = line_minx;
+ update_rect.y = line_miny;
+ update_rect.width = (line_maxx - line_minx) + 1;
+ update_rect.height = (line_maxy - line_miny) + 1;
+ gtk_widget_draw (da, &update_rect);
+ }
+ line_batch_mode = BATCH_OFF;
+}
+
+void line (int x1, int y1, int x2, int y2, enum view1_line_fn function)
+{
+ GdkRectangle update_rect;
+ GdkGC *gc = NULL;
+
+ switch(function) {
+ case LINE_DRAW_BLACK:
+ gc = da->style->black_gc;
+ break;
+
+ case LINE_DRAW_WHITE:
+ gc = da->style->white_gc;
+ break;
+
+ case LINE_PRINT:
+ line_print (x1, y1, x2, y2);
+ return;
+ }
+
+ gdk_draw_line (pm, gc, x1, y1, x2, y2);
+
+ switch (line_batch_mode) {
+ case BATCH_OFF:
+ update_rect.x = x1;
+ update_rect.y = y1;
+ update_rect.width = (x2-x1) + 1;
+ update_rect.height = (y2-y1) + 1;
+ gtk_widget_draw (da, &update_rect);
+ break;
+
+ case BATCH_NEW:
+ line_minx = x1;
+ line_maxx = x2;
+ line_miny = y1;
+ line_maxy = y2;
+ line_batch_mode = BATCH_EXISTING;
+ line_batch_count = 1;
+ break;
+
+ case BATCH_EXISTING:
+ if (line_minx > x1)
+ line_minx = x1;
+ if (line_miny > y1)
+ line_miny = y1;
+ if (line_maxx < x2)
+ line_maxx = x2;
+ if (line_maxy < y2)
+ line_maxy = y2;
+ line_batch_count++;
+ break;
+ }
+}
+
+
+/****************************************************************************
+* display_pid_axis
+****************************************************************************/
+
+static void display_pid_axis(v1_geometry_t *vp)
+{
+ int y, i, label_tick;
+ int last_printed_y = -vp->strip_height;
+ pid_sort_t *pp;
+ int pid_index;
+ char *label_fmt;
+ char tmpbuf [128];
+
+ /* No pids yet? Outta here */
+ if (g_pids == NULL)
+ return;
+
+ line_batch_start();
+
+ for (i = 0; i < vp->npids; i++) {
+ pid_index = vp->first_pid_index + i;
+ if (pid_index >= g_npids)
+ break;
+
+ set_color(pid_index);
+ pp = (g_pids + pid_index);
+
+ label_fmt = get_track_label(pp->pid_value);
+ snprintf(tmpbuf, sizeof(tmpbuf)-1, label_fmt, pp->pid_value);
+
+ y = i*vp->strip_height + vp->pid_ax_offset;
+
+ /*
+ * Have we incremented enough space to have another label not
+ * overlap the previous label?
+ */
+ if (y - last_printed_y > 9) {
+ /* Draw label */
+ tbox(tmpbuf, 0, y +4, TBOX_DRAW_PLAIN+s_print_offset);
+
+ last_printed_y = y;
+
+ /*
+ * And let the line stick out a bit more to indicate this label
+ * relates to the following line.
+ */
+ label_tick = 4;
+ }
+ else {
+ label_tick = 0;
+ }
+
+ /* Draw axis line, but only if the lines aren't too close together */
+ if (vp->strip_height > 4) {
+ line(vp->pid_ax_width - label_tick, y+4*s_print_offset,
+ vp->total_width, y+4*s_print_offset,
+ LINE_DRAW_BLACK+s_print_offset);
+ }
+ }
+
+ set_color(COLOR_DEFAULT);
+ line_batch_end();
+}
+
+/****************************************************************************
+* view1_read_events_callback
+* New event data just showed up, reset a few things.
+****************************************************************************/
+
+void view1_read_events_callback(void)
+{
+ int max_vis_index;
+
+ s_v1->first_pid_index = 0;
+
+ max_vis_index = 300;
+ if (max_vis_index > g_nevents)
+ max_vis_index = g_nevents-1;
+
+ s_v1->minvistime = 0LL;
+ s_v1->maxvistime = (g_events[g_nevents - 1].time * 9)/ 8;
+ s_srchindex = 0;
+ s_srchcode = 0;
+ s_last_selected_event = 0;
+
+ init_track_colors();
+
+ recompute_hscrollbar();
+ recompute_vscrollbar();
+}
+
+/****************************************************************************
+* display_event_data
+****************************************************************************/
+
+static void display_event_data(v1_geometry_t *vp)
+{
+ int start_index;
+ int pid_index;
+ int x, y;
+ event_t *ep;
+ event_def_t *edp;
+ double time_per_pixel;
+ char tmpbuf[1024];
+ GdkRectangle *print_rect;
+ int *last_x_used;
+
+ /* Happens if one loads the event def header first, for example. */
+ if (g_nevents == 0)
+ return;
+
+ time_per_pixel = dtime_per_pixel(vp);
+
+ start_index = find_event_index (vp->minvistime);
+
+ /* Scrolled too far right? */
+ if (start_index >= g_nevents)
+ return;
+
+ ep = (g_events + start_index);
+
+ if (s_print_offset || summary_mode) {
+ last_x_used = (int *)g_malloc0(vp->npids * sizeof(int));
+ } else {
+ last_x_used = NULL;
+ }
+
+ line_batch_start();
+
+ while (ep < (g_events + g_nevents) &&
+ (ep->time < vp->maxvistime)) {
+ pid_index = ep->pid->pid_index;
+ set_color(pid_index);
+
+ /* First filter: pid out of range */
+ if ((pid_index < vp->first_pid_index) ||
+ (pid_index >= vp->first_pid_index + vp->npids)) {
+ ep++;
+ continue;
+ }
+
+ /* Second filter: event hidden */
+ edp = find_event_definition (ep->code);
+ if (!edp->selected) {
+ ep++;
+ continue;
+ }
+
+ /* Display it... */
+
+ pid_index -= vp->first_pid_index;
+
+ y = pid_index*vp->strip_height + vp->event_offset;
+
+ x = vp->pid_ax_width +
+ (int)(((double)(ep->time - vp->minvistime)) / time_per_pixel);
+
+ if (last_x_used != NULL && x < last_x_used[pid_index]) {
+ ep++;
+ continue;
+ }
+
+ if (ep->flags & (EVENT_FLAG_SELECT | EVENT_FLAG_SEARCHRSLT)) {
+ if (ep->flags & EVENT_FLAG_SELECT) {
+ format_popbox_string(tmpbuf, sizeof(tmpbuf), ep, edp);
+#ifdef NOTDEF
+ sprintf(tmpbuf, edp->name);
+ sprintf(tmpbuf+strlen(tmpbuf), ": ");
+ sprintf(tmpbuf+strlen(tmpbuf), edp->format, ep->datum);
+#endif
+ } else {
+ sprintf(tmpbuf, "SEARCH RESULT");
+ }
+ print_rect = tbox(tmpbuf, x, y - vp->pop_offset,
+ TBOX_DRAW_BOXED+s_print_offset);
+ line(x, y-vp->pop_offset, x, y, LINE_DRAW_BLACK+s_print_offset);
+ if (last_x_used != NULL)
+ last_x_used[pid_index] = x + print_rect->width;
+ }
+ if (summary_mode) {
+ int delta = vp->strip_height / 3;
+ if (delta < 1)
+ delta = 1;
+ y = pid_index*vp->strip_height + vp->pid_ax_offset;
+ line(x, y - delta, x, y + delta, LINE_DRAW_BLACK);
+ last_x_used[pid_index] = x + 1;
+ } else {
+ sprintf(tmpbuf, "%ld", ep->code);
+ print_rect = tbox(tmpbuf, x, y, TBOX_DRAW_EVENT+s_print_offset);
+ if (last_x_used != NULL)
+ last_x_used[pid_index] = x + print_rect->width;
+ }
+
+ ep++;
+ }
+ if (last_x_used)
+ g_free(last_x_used);
+ line_batch_end();
+ set_color(COLOR_DEFAULT);
+}
+
+/****************************************************************************
+* display_clear
+****************************************************************************/
+
+static void display_clear(void)
+{
+ GdkRectangle update_rect;
+
+ gdk_draw_rectangle (pm, da->style->white_gc, TRUE,
+ 0, 0, da->allocation.width,
+ da->allocation.height);
+
+ update_rect.x = 0;
+ update_rect.y = 0;
+ update_rect.width = da->allocation.width;
+ update_rect.height = da->allocation.height;
+
+ gtk_widget_draw (da, &update_rect);
+}
+
+/****************************************************************************
+* display_time_axis
+****************************************************************************/
+
+static void display_time_axis(v1_geometry_t *vp)
+{
+ int x, y, i;
+ int xoffset, nticks;
+ char tmpbuf [128];
+ double unit_divisor;
+ double time;
+ char *units;
+ double time_per_pixel;
+
+ y = vp->npids * vp->strip_height + vp->pid_ax_offset;
+
+ x = vp->pid_ax_width;
+
+ nticks = (vp->total_width - vp->pid_ax_width) / vp->time_ax_spacing;
+
+ time_per_pixel = dtime_per_pixel(vp);
+
+ units = "ns";
+ unit_divisor = 1.00;
+
+ if ((vp->maxvistime / unit_divisor) > 1000) {
+ units = "us";
+ unit_divisor = 1000.00;
+ }
+
+ if ((vp->maxvistime / unit_divisor) > 1000) {
+ units = "ms";
+ unit_divisor = 1000.00*1000.00;
+ }
+ if ((vp->maxvistime / unit_divisor) > 1000) {
+ units = "s";
+ unit_divisor = 1000.00*1000.00*1000.00;
+ }
+
+ /* Draw line */
+ line(x, y, vp->total_width, y, LINE_DRAW_BLACK+s_print_offset);
+
+ xoffset = 0;
+
+ for (i = 0; i < nticks; i++) {
+ /* Tick mark */
+ line(x+xoffset, y-3, x+xoffset, y+3, LINE_DRAW_BLACK+s_print_offset);
+
+ time = (double)(x + xoffset - vp->pid_ax_width);
+ time *= time_per_pixel;
+ time += (double)(vp->minvistime);
+ time /= unit_divisor;
+
+ sprintf (tmpbuf, "%.2f%s", time, units);
+
+ tbox(tmpbuf, x+xoffset, y+15, TBOX_DRAW_PLAIN+s_print_offset);
+
+ xoffset += vp->time_ax_spacing;
+ }
+}
+
+/****************************************************************************
+* clear_scoreboard
+* Forget about any temporary displays, they're gone now...
+****************************************************************************/
+
+static void clear_scoreboard(void)
+{
+ s_result_up = FALSE;
+}
+
+/****************************************************************************
+* view1_display
+****************************************************************************/
+
+void view1_display(void)
+{
+ display_clear();
+ display_pid_axis(s_v1);
+ display_event_data(s_v1);
+ display_time_axis(s_v1);
+ clear_scoreboard();
+}
+
+static gint idle_tag;
+
+/****************************************************************************
+* view1_display_eventually
+****************************************************************************/
+
+static void view1_display_eventually(void)
+{
+ gtk_idle_remove(idle_tag);
+ idle_tag = 0;
+ view1_display();
+}
+
+
+/****************************************************************************
+* view1_display_when_idle
+****************************************************************************/
+
+void view1_display_when_idle(void)
+{
+ if (idle_tag == 0) {
+ idle_tag = gtk_idle_add((GtkFunction) view1_display_eventually, 0);
+ }
+}
+
+/****************************************************************************
+* view1_about
+****************************************************************************/
+
+void view1_about (char *tmpbuf)
+{
+ int nsnaps;
+ snapshot_t *snaps;
+
+ sprintf(tmpbuf+strlen(tmpbuf), "Minvistime %lld\nMaxvistime %lld\n",
+ s_v1->minvistime, s_v1->maxvistime);
+ sprintf(tmpbuf+strlen(tmpbuf), "Strip Height %d\n",
+ s_v1->strip_height);
+
+ for (nsnaps = 0, snaps = s_snapshots; snaps; snaps = snaps->next) {
+ nsnaps++;
+ }
+ sprintf(tmpbuf+strlen(tmpbuf), "%d snapshots in the ring\n", nsnaps);
+}
diff --git a/perftool/Makefile.am b/perftool/Makefile.am
new file mode 100644
index 00000000000..ac652aa6a2c
--- /dev/null
+++ b/perftool/Makefile.am
@@ -0,0 +1,44 @@
+# Copyright (c) 2016 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+AUTOMAKE_OPTIONS = foreign
+AM_CFLAGS = -Wall
+
+bin_PROGRAMS = c2cpel cpelatency cpeldump cpelinreg cpelstate
+
+lib_LTLIBRARIES = libcperf.la
+
+libcperf_la_SOURCES = delsvec.c linreg.c props.c cpel_util.c
+
+TOOL_LIBS = libcperf.la -lvppinfra -lm
+
+c2cpel_SOURCE = c2cpel.c
+c2cpel_LDADD = $(TOOL_LIBS)
+
+cpelatency_SOURCE = cpelatency.c
+cpelatency_LDADD = $(TOOL_LIBS)
+
+cpeldump_SOURCE = cpeldump.c
+cpeldump_LDADD = $(TOOL_LIBS)
+
+cpelinreg_SOURCE = cpelinreg.c
+cpelinreg_LDADD = $(TOOL_LIBS)
+
+cpelstate_SOURCE = cpelstate.c
+cpelstate_LDADD = $(TOOL_LIBS)
+
+
+
+
+
+
diff --git a/perftool/c2cpel.c b/perftool/c2cpel.c
new file mode 100644
index 00000000000..38e6fe52e55
--- /dev/null
+++ b/perftool/c2cpel.c
@@ -0,0 +1,248 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2006-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <vppinfra/clib.h>
+#include <vppinfra/vec.h>
+#include <vppinfra/hash.h>
+#include <vppinfra/elog.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <time.h>
+#include "cpel.h"
+#include "cpel_util.h"
+
+static elog_main_t elog_main;
+
+/*
+ * convert_clib_file
+ */
+void convert_clib_file(char *clib_file)
+{
+ clib_error_t *error = 0;
+ int i;
+ elog_main_t *em = &elog_main;
+ double starttime, delta;
+
+ error = elog_read_file (&elog_main, clib_file);
+
+ if (error) {
+ clib_warning("%U", format_clib_error, error);
+ exit (1);
+ }
+
+ em = &elog_main;
+
+ starttime = em->events[0].time;
+
+ for (i = 0; i < vec_len (em->events); i++) {
+ elog_event_t *e; /* clib event */
+ evt_t *ep; /* xxx2cpel event */
+ u8 *s;
+ u64 timestamp;
+ elog_event_type_t *t;
+ u8 *brief_event_name;
+ u8 *track_name;
+ int j;
+
+ e = vec_elt_at_index(em->events, i);
+
+ /* Seconds since start of log */
+ delta = e->time - starttime;
+
+ /* u64 nanoseconds since start of log */
+ timestamp = delta * 1e9;
+
+ s = format (0, "%U%c", format_elog_event, em, e, 0);
+
+ /* allocate an event instance */
+ vec_add2(the_events, ep, 1);
+ ep->timestamp = timestamp;
+
+ /* convert string event code to a real number */
+ t = vec_elt_at_index (em->event_types, e->type);
+
+ /*
+ * Construct a reasonable event name.
+ * Truncate the format string at the first whitespace break
+ * or printf format character.
+ */
+ brief_event_name = format (0, "%s", t->format);
+
+ for (j = 0; j < vec_len (brief_event_name); j++) {
+ if (brief_event_name[j] == ' ' ||
+ brief_event_name[j] == '%' ||
+ brief_event_name[j] == '(') {
+ brief_event_name[j] = 0;
+ break;
+ }
+ }
+ /* Throw away that much of the formatted event */
+ vec_delete (s, j+1, 0);
+
+ ep->event_id = find_or_add_event(brief_event_name, "%s");
+
+ track_name = format (0, "%U%c", format_elog_track, em, e, 0);
+
+ ep->track_id = find_or_add_track (track_name);
+
+ ep->datum = find_or_add_strtab(s);
+
+ vec_free (track_name);
+ vec_free(brief_event_name);
+ vec_free(s);
+ }
+}
+
+u8 *vec_basename (char *s)
+{
+ u8 * rv;
+ char *cp = s;
+
+ while (*cp)
+ cp++;
+
+ cp--;
+
+ while (cp > s && *cp != '/')
+ cp--;
+
+ if (cp > s)
+ cp++;
+
+ rv = format (0, "%s", cp);
+ return rv;
+}
+
+
+int event_compare (const void *a0, const void *a1)
+{
+ evt_t *e0 = (evt_t *)a0;
+ evt_t *e1 = (evt_t *)a1;
+
+ if (e0->timestamp < e1->timestamp)
+ return -1;
+ else if (e0->timestamp > e1->timestamp)
+ return 1;
+ return 0;
+}
+
+int main (int argc, char **argv)
+{
+ int curarg=1;
+ char **inputfiles = 0;
+ char *outputfile = 0;
+ FILE *ofp;
+
+ if (argc < 3)
+ goto usage;
+
+ while (curarg < argc) {
+ if (!strncmp(argv[curarg], "--input-file", 3)) {
+ curarg++;
+ if (curarg < argc) {
+ vec_add1 (inputfiles, argv[curarg]);
+ curarg++;
+ continue;
+ }
+ clib_warning("Missing filename after --input-file\n");
+ exit (1);
+ }
+
+ if (!strncmp(argv[curarg], "--output-file", 3)) {
+ curarg ++;
+ if (curarg < argc) {
+ outputfile = argv[curarg];
+ curarg ++;
+ continue;
+ }
+ clib_warning("Missing filename after --output-file\n");
+ exit(1);
+ }
+ vec_add1 (inputfiles, argv[curarg]);
+ curarg++;
+ continue;
+
+ usage:
+ fformat(stderr,
+ "c2cpel [--input-file] <filename> --output-file <filename>\n");
+ exit(1);
+ }
+
+ if (vec_len(inputfiles) == 0 || outputfile == 0)
+ goto usage;
+
+ if (vec_len(inputfiles) > 1)
+ goto usage;
+
+ cpel_util_init();
+
+ convert_clib_file (inputfiles[0]);
+
+ ofp = fopen (outputfile, "w");
+ if (ofp == NULL) {
+ clib_unix_warning ("couldn't create %s", outputfile);
+ exit (1);
+ }
+
+ alpha_sort_tracks();
+ fixup_event_tracks();
+
+ /*
+ * Four sections: string-table, event definitions, track defs, events.
+ */
+ if (!write_cpel_header(ofp, 4)) {
+ clib_warning ("Error writing cpel header to %s...\n", outputfile);
+ unlink(outputfile);
+ exit(1);
+ }
+
+ if (!write_string_table(ofp)) {
+ clib_warning ("Error writing string table to %s...\n", outputfile);
+ unlink(outputfile);
+ exit(1);
+ }
+
+ if (!write_event_defs(ofp)) {
+ clib_warning ("Error writing event defs to %s...\n", outputfile);
+ unlink(outputfile);
+ exit(1);
+ }
+
+ if (!write_track_defs(ofp)) {
+ clib_warning ("Error writing track defs to %s...\n", outputfile);
+ unlink(outputfile);
+ exit(1);
+ }
+
+ if (!write_events(ofp, (u64) 1e9)) {
+ clib_warning ("Error writing events to %s...\n", outputfile);
+ unlink(outputfile);
+ exit(1);
+
+ }
+ fclose(ofp);
+ exit (0);
+}
diff --git a/perftool/configure.ac b/perftool/configure.ac
new file mode 100644
index 00000000000..37a4174f1df
--- /dev/null
+++ b/perftool/configure.ac
@@ -0,0 +1,12 @@
+AC_INIT(perftool, 2.0)
+AM_INIT_AUTOMAKE
+
+AC_CHECK_LIB([vppinfra], [clib_mem_get_page_size],,
+ AC_MSG_ERROR([Please install the vpp-lib package]))
+AC_CHECK_HEADER([vppinfra/clib.h],,
+ AC_MSG_ERROR([Please install the vpp-dev package]))
+
+AM_PROG_LIBTOOL
+AM_PROG_CC_C_O
+
+AC_OUTPUT([Makefile])
diff --git a/perftool/cpel.h b/perftool/cpel.h
new file mode 100644
index 00000000000..0bfb1a68ef2
--- /dev/null
+++ b/perftool/cpel.h
@@ -0,0 +1,83 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2005-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CPEL_H_
+#define _CPEL_H_ 1
+
+typedef struct cpel_file_header_ {
+ unsigned char endian_version;
+ unsigned char pad;
+ unsigned short nsections;
+ unsigned int file_date;
+} cpel_file_header_t;
+
+#define CPEL_FILE_LITTLE_ENDIAN 0x80
+#define CPEL_FILE_VERSION 0x01
+#define CPEL_FILE_VERSION_MASK 0x7F
+
+typedef struct cpel_section_header_ {
+ unsigned int section_type;
+ unsigned int data_length; /* does NOT include type and itself */
+} cpel_section_header_t;
+
+#define CPEL_SECTION_STRTAB 1
+/* string at offset 0 is the name of the table */
+
+#define CPEL_SECTION_SYMTAB 2
+#define CPEL_SECTION_EVTDEF 3
+
+typedef struct event_definition_section_header_ {
+ char string_table_name[64];
+ unsigned int number_of_event_definitions;
+} event_definition_section_header_t;
+
+typedef struct event_definition_ {
+ unsigned int event;
+ unsigned int event_format;
+ unsigned int datum_format;
+} event_definition_t;
+
+#define CPEL_SECTION_TRACKDEF 4
+
+typedef struct track_definition_section_header_ {
+ char string_table_name[64];
+ unsigned int number_of_track_definitions;
+} track_definition_section_header_t;
+
+typedef struct track_definition_ {
+ unsigned int track;
+ unsigned int track_format;
+} track_definition_t;
+
+#define CPEL_SECTION_EVENT 5
+
+typedef struct event_section_header_ {
+ char string_table_name[64];
+ unsigned int number_of_events;
+ unsigned int clock_ticks_per_second;
+} event_section_header_t;
+
+typedef struct event_entry_ {
+ unsigned int time[2];
+ unsigned int track;
+ unsigned int event_code;
+ unsigned int event_datum;
+} event_entry_t;
+
+#define CPEL_NUM_SECTION_TYPES 5
+
+#endif /* _CPEL_H_ */
+
diff --git a/perftool/cpel_util.c b/perftool/cpel_util.c
new file mode 100644
index 00000000000..7ee9b6e2a00
--- /dev/null
+++ b/perftool/cpel_util.c
@@ -0,0 +1,456 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2006-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <vppinfra/clib.h>
+#include <vppinfra/vec.h>
+#include <vppinfra/hash.h>
+#include <vppinfra/bitmap.h>
+#include <vppinfra/byte_order.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <time.h>
+#include "cpel.h"
+#include "cpel_util.h"
+
+evt_t *the_events;
+
+track_t *the_tracks;
+u32 *track_alpha_map;
+
+event_definition_t *the_event_definitions;
+i64 min_timestamp;
+
+/* Hash tables, used to find previous instances of the same items */
+uword *the_track_hash;
+uword *the_msg_event_hash;
+uword *the_strtab_hash;
+uword *the_pidtid_hash;
+uword *the_pid_to_name_hash;
+u8 *the_strtab;
+
+uword *the_event_id_bitmap;
+
+/*
+ * find_or_add_strtab
+ * Finds or adds a string to the string table
+ */
+u32 find_or_add_strtab(void *s_arg)
+{
+ uword *p;
+ int len;
+ u8 *this_string;
+ u8 *scopy=0;
+ char *s = s_arg;
+
+ p = hash_get_mem(the_strtab_hash, s);
+ if (p) {
+ return (p[0]);
+ }
+
+ /*
+ * Here's a CLIB bear-trap. We can't add the string-table
+ * strings to the to the hash table (directly), since it
+ * expands and moves periodically. All of the hash table
+ * entries turn into dangling references, yadda yadda.
+ */
+
+ len = strlen(s)+1;
+ vec_add2(the_strtab, this_string, len);
+ memcpy(this_string, s, len);
+
+ /* Make a copy which won't be moving around... */
+ vec_validate(scopy, len);
+ memcpy(scopy, s, len);
+
+ hash_set_mem(the_strtab_hash, scopy, this_string - the_strtab);
+
+ return(this_string - the_strtab);
+}
+
+/*
+ * find_or_add_track
+ * returns index in track table
+ */
+u32 find_or_add_track(void *s_arg)
+{
+ uword *p;
+ track_t *this_track;
+ u8 *copy_s;
+ char *s=s_arg;
+
+ p = hash_get_mem(the_track_hash, s);
+ if (p) {
+ return (p[0]);
+ }
+ vec_add2(the_tracks, this_track, 1);
+
+ this_track->original_index = this_track - the_tracks;
+ this_track->strtab_offset = find_or_add_strtab(s);
+
+ copy_s = (u8 *)vec_dup(s);
+
+ hash_set_mem(the_track_hash, copy_s, this_track - the_tracks);
+ return(this_track - the_tracks);
+}
+
+/*
+ * find_or_add_event
+ * Adds an event to the event definition vector and add it to
+ * the event hash table
+ */
+
+u32 find_or_add_event(void *s_arg, char *datum_format)
+{
+ uword *p;
+ u8 *copy_s;
+ event_definition_t *this_event_definition;
+ u32 event_id;
+ char *s=s_arg;
+
+ p = hash_get_mem(the_msg_event_hash, s);
+ if (p) {
+ return (p[0]);
+ }
+ vec_add2(the_event_definitions, this_event_definition, 1);
+
+ /* Allocate a new event-id */
+ event_id = clib_bitmap_first_clear (the_event_id_bitmap);
+ the_event_id_bitmap = clib_bitmap_set(the_event_id_bitmap, event_id, 1);
+ this_event_definition->event = event_id;
+ this_event_definition->event_format = find_or_add_strtab(s);
+ this_event_definition->datum_format = find_or_add_strtab(datum_format);
+
+ copy_s = (u8 *)vec_dup(s);
+
+ hash_set_mem(the_msg_event_hash, copy_s, event_id);
+
+ return(event_id);
+}
+
+/*
+ * write_string_table
+ */
+int write_string_table(FILE *ofp)
+{
+ cpel_section_header_t sh;
+
+ /* Round up string table size */
+ while (vec_len(the_strtab) & 0x7)
+ vec_add1(the_strtab, 0);
+
+ sh.section_type = ntohl(CPEL_SECTION_STRTAB);
+ sh.data_length = ntohl(vec_len(the_strtab));
+
+ if (fwrite(&sh, sizeof(sh), 1, ofp) != 1)
+ return(0);
+
+ if (fwrite(the_strtab, 1, vec_len(the_strtab), ofp) !=
+ vec_len(the_strtab))
+ return(0);
+
+ return(1);
+}
+
+/*
+ * write_cpel_header
+ */
+int write_cpel_header(FILE *ofp, u32 nsections)
+{
+ cpel_file_header_t h;
+
+ h.endian_version = CPEL_FILE_VERSION;
+ h.pad = 0;
+ h.nsections = ntohs(nsections);
+ h.file_date = ntohl(time(0));
+ if (fwrite(&h, sizeof(h), 1, ofp) != 1)
+ return (0);
+
+ return(1);
+}
+
+/*
+ * write_event_defs
+ */
+int write_event_defs(FILE *ofp)
+{
+ cpel_section_header_t sh;
+ event_definition_section_header_t edsh;
+ event_definition_t *this_event_definition;
+ int i;
+
+ /* Next, the event definitions */
+ sh.section_type = ntohl(CPEL_SECTION_EVTDEF);
+ sh.data_length = ntohl(vec_len(the_event_definitions)
+ *sizeof(the_event_definitions[0])
+ + sizeof(event_definition_section_header_t));
+
+ if (fwrite(&sh, sizeof(sh), 1, ofp) != 1)
+ return(0);
+
+ memset(&edsh, 0, sizeof(edsh));
+
+ strcpy(edsh.string_table_name, "FileStrtab");
+ edsh.number_of_event_definitions = ntohl(vec_len(the_event_definitions));
+
+ if (fwrite(&edsh, sizeof(edsh), 1, ofp) != 1)
+ return(0);
+
+ for (i = 0; i < vec_len(the_event_definitions); i++) {
+ this_event_definition = &the_event_definitions[i];
+ /* Endian fixup */
+ this_event_definition->event = ntohl(this_event_definition->event);
+ this_event_definition->event_format =
+ ntohl(this_event_definition->event_format);
+ this_event_definition->datum_format =
+ ntohl(this_event_definition->datum_format);
+
+ if (fwrite(this_event_definition, sizeof(the_event_definitions[0]),
+ 1, ofp) != 1)
+ return(0);
+ }
+ return(1);
+}
+
+/*
+ * ntohll
+ */
+u64 ntohll (u64 x) {
+ if (clib_arch_is_little_endian)
+ x = ((((x >> 0) & 0xff) << 56)
+ | (((x >> 8) & 0xff) << 48)
+ | (((x >> 16) & 0xff) << 40)
+ | (((x >> 24) & 0xff) << 32)
+ | (((x >> 32) & 0xff) << 24)
+ | (((x >> 40) & 0xff) << 16)
+ | (((x >> 48) & 0xff) << 8)
+ | (((x >> 56) & 0xff) << 0));
+
+ return x;
+}
+
+/*
+ * write_events
+ */
+int write_events(FILE *ofp, u64 clock_ticks_per_second)
+{
+ cpel_section_header_t sh;
+ event_section_header_t eh;
+ u32 number_of_events;
+ int i;
+ event_entry_t e;
+ u64 net_timestamp;
+ evt_t *this_event;
+ u32 time0, time1;
+
+ number_of_events = vec_len(the_events);
+
+ sh.section_type = ntohl(CPEL_SECTION_EVENT);
+ sh.data_length = ntohl(number_of_events * sizeof(e) +
+ sizeof(event_section_header_t));
+
+ if (fwrite(&sh, sizeof(sh), 1, ofp) != 1)
+ return(0);
+
+ memset(&eh, 0, sizeof(eh));
+ strcpy(eh.string_table_name, "FileStrtab");
+ eh.number_of_events = ntohl(number_of_events);
+ eh.clock_ticks_per_second = ntohl(clock_ticks_per_second);
+
+ if (fwrite(&eh, sizeof(eh), 1, ofp) != 1)
+ return(0);
+
+ for (i = 0; i < number_of_events; i++) {
+ this_event = &the_events[i];
+ net_timestamp = ntohll(this_event->timestamp);
+
+ time1 = net_timestamp>>32;
+ time0 = net_timestamp & 0xFFFFFFFF;
+
+ e.time[0] = time0;
+ e.time[1] = time1;
+ e.track = ntohl(this_event->track_id);
+ e.event_code = ntohl(this_event->event_id);
+ e.event_datum = ntohl(this_event->datum);
+
+ if (fwrite(&e, sizeof(e), 1, ofp) != 1)
+ return(0);
+ }
+ return(1);
+}
+
+/*
+ * write_track_defs
+ */
+int write_track_defs(FILE *ofp)
+{
+ cpel_section_header_t sh;
+ track_definition_section_header_t tdsh;
+ track_definition_t record;
+ track_definition_t *this_track_definition = &record;
+ int i;
+ event_definition_section_header_t edsh;
+
+ /* Next, the event definitions */
+ sh.section_type = ntohl(CPEL_SECTION_TRACKDEF);
+ sh.data_length = ntohl(vec_len(the_tracks)
+ *sizeof(this_track_definition[0])
+ + sizeof(track_definition_section_header_t));
+
+ if (fwrite(&sh, sizeof(sh), 1, ofp) != 1)
+ return(0);
+
+ memset(&tdsh, 0, sizeof(tdsh));
+
+ strcpy(tdsh.string_table_name, "FileStrtab");
+ tdsh.number_of_track_definitions = ntohl(vec_len(the_tracks));
+
+ if (fwrite(&tdsh, sizeof(edsh), 1, ofp) != 1)
+ return(0);
+
+ for (i = 0; i < vec_len(the_tracks); i++) {
+ this_track_definition->track = ntohl(i);
+ this_track_definition->track_format =
+ ntohl(the_tracks[i].strtab_offset);
+
+ if (fwrite(this_track_definition, sizeof(this_track_definition[0]),
+ 1, ofp) != 1)
+ return(0);
+ }
+ return(1);
+}
+
+void cpel_util_init (void)
+{
+ u8 *eventstr;
+
+ the_strtab_hash = hash_create_string (0, sizeof (uword));
+ the_msg_event_hash = hash_create_string (0, sizeof (uword));
+ the_track_hash = hash_create_string (0, sizeof (uword));
+ the_pidtid_hash = hash_create_string (0, sizeof(uword));
+ the_pid_to_name_hash = hash_create(0, sizeof(uword));
+
+ /* Must be first, or no supper... */
+ find_or_add_strtab("FileStrtab");
+
+ /* Historical canned events, no longer used. */
+ if (0) {
+ /* event 0 (not used) */
+ eventstr = format(0, "PlaceholderNotUsed");
+ vec_add1(eventstr, 0);
+ find_or_add_event(eventstr, "%s");
+ vec_free(eventstr);
+
+ /* event 1 (thread on CPU) */
+ eventstr = format(0, "THREAD/THRUNNING");
+ vec_add1(eventstr, 0);
+ find_or_add_event(eventstr, "%s");
+ vec_free(eventstr);
+
+ /* event 2 (thread ready) */
+ eventstr = format(0, "THREAD/THREADY");
+ vec_add1(eventstr, 0);
+ find_or_add_event(eventstr, "%s");
+ vec_free(eventstr);
+
+ /* event 3 (function enter) */
+ eventstr = format(0, "FUNC/ENTER");
+ vec_add1(eventstr, 0);
+ find_or_add_event(eventstr, "0x%x");
+ vec_free(eventstr);
+
+ /* event 4 (function enter) */
+ eventstr = format(0, "FUNC/EXIT");
+ vec_add1(eventstr, 0);
+ find_or_add_event(eventstr, "0x%x");
+ vec_free(eventstr);
+ }
+}
+
+/*
+ * alpha_compare_tracks
+ */
+static int alpha_compare_tracks(const void *a1, const void *a2)
+{
+ int i;
+ track_t *t1 = (track_t *)a1;
+ track_t *t2 = (track_t *)a2;
+ u8 *s1 = &the_strtab[t1->strtab_offset];
+ u8 *s2 = &the_strtab[t2->strtab_offset];
+
+ for (i = 0; s1[i] && s2[i]; i++) {
+ if (s1[i] < s2[i])
+ return(-1);
+ if (s1[i] > s2[i])
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * alpha_sort_tracks
+ * Alphabetically sort tracks, set up a mapping
+ * vector so we can quickly map the original track index to
+ * the new/improved/alpha-sorted index
+ */
+void alpha_sort_tracks(void)
+{
+ track_t *this_track;
+ int i;
+
+ qsort(the_tracks, vec_len(the_tracks), sizeof(track_t),
+ alpha_compare_tracks);
+
+ vec_validate(track_alpha_map, vec_len(the_tracks));
+ _vec_len(track_alpha_map) = vec_len(the_tracks);
+
+ for (i = 0; i < vec_len(the_tracks); i++) {
+ this_track = &the_tracks[i];
+ track_alpha_map[this_track->original_index] = i;
+ }
+}
+
+/*
+ * fixup_event_tracks
+ * Use the track alpha mapping to account for the alphabetic
+ * sort performed by the previous routine
+ */
+void fixup_event_tracks(void)
+{
+ int i;
+ u32 old_track;
+
+ for (i = 0; i < vec_len(the_events); i++) {
+ old_track = the_events[i].track_id;
+ the_events[i].track_id = track_alpha_map[old_track];
+ }
+}
+
+/* Indispensable for debugging in gdb... */
+
+u32 vl(void *x)
+{
+ return vec_len(x);
+}
diff --git a/perftool/cpel_util.h b/perftool/cpel_util.h
new file mode 100644
index 00000000000..b76f7a4b322
--- /dev/null
+++ b/perftool/cpel_util.h
@@ -0,0 +1,68 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2006-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __cpel_util_h__
+#define __cpel_util_h__
+
+/*
+ * Our idea of an event, as opposed to a CPEL event
+ */
+typedef struct evt_ {
+ u64 timestamp;
+ u32 track_id;
+ u32 event_id;
+ u32 datum;
+} evt_t;
+
+evt_t *the_events;
+
+/*
+ * Track object, so we can sort the tracks alphabetically and
+ * fix the events later
+ */
+typedef struct track_ {
+ u32 original_index;
+ u32 strtab_offset;
+} track_t;
+
+track_t *the_tracks;
+u32 *track_alpha_map;
+
+event_definition_t *the_event_definitions;
+i64 min_timestamp;
+
+/* Hash tables, used to find previous instances of the same items */
+uword *the_track_hash;
+uword *the_msg_event_hash;
+uword *the_strtab_hash;
+uword *the_pidtid_hash;
+uword *the_pid_to_name_hash;
+u8 *the_strtab;
+
+u32 find_or_add_strtab(void *s_arg);
+u32 find_or_add_track(void *s_arg);
+u32 find_or_add_event(void *s_arg, char *datum_format);
+int write_string_table(FILE *ofp);
+int write_cpel_header(FILE *ofp, u32 nsections);
+int write_event_defs(FILE *ofp);
+u64 ntohll (u64 x);
+int write_events(FILE *ofp, u64 clock_ticks_per_second);
+int write_track_defs(FILE *ofp);
+void cpel_util_init (void);
+void alpha_sort_tracks(void);
+void fixup_event_tracks(void);
+
+#endif /* __cpel_util_h__ */
diff --git a/perftool/cpelatency.c b/perftool/cpelatency.c
new file mode 100644
index 00000000000..f40a1fb1c84
--- /dev/null
+++ b/perftool/cpelatency.c
@@ -0,0 +1,927 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2006-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <vppinfra/clib.h>
+#include <vppinfra/vec.h>
+#include <vppinfra/hash.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <time.h>
+#include "cpel.h"
+#include <math.h>
+
+char *time_format = "%.03d:%.02d:%.02d:%.03d:%.03d ";
+static char version[] = "cpelatency 2.0";
+
+#define USEC_PER_MS 1000LL
+#define USEC_PER_SECOND (1000*USEC_PER_MS)
+#define USEC_PER_MINUTE (60*USEC_PER_SECOND)
+#define USEC_PER_HOUR (60*USEC_PER_MINUTE)
+
+uword *the_strtab_hash; /* (name, base-VA) hash of all string tables */
+uword *the_evtdef_hash; /* (event-id, event-definition) hash */
+uword *the_trackdef_hash; /* (track-id, track-definition) hash */
+uword *the_pidtid_hash; /* ("pid:xxx tid:yy", track-definition) hash */
+
+f64 ticks_per_us;
+u32 start_event_code = 2; /* default: XR thread ready event */
+u32 end_event_code = 1; /* default: XR thread running event */
+int exclude_kernel_from_summary_stats=1;
+int summary_stats_only;
+int scatterplot;
+u8 *name_filter;
+int have_trackdefs;
+
+typedef enum {
+ SORT_MAX_TIME=1,
+ SORT_MAX_OCCURRENCES,
+ SORT_NAME,
+} sort_t;
+
+sort_t sort_type = SORT_MAX_TIME;
+
+int widest_name_format=5;
+int widest_track_format=20;
+
+typedef struct bound_event_ {
+ u32 event_code;
+ u8 *event_str;
+ u8 *datum_str;
+ u32 is_strtab_ref;
+} bound_event_t;
+
+bound_event_t *bound_events;
+
+typedef struct bound_track_ {
+ u32 track;
+ u8 *track_str;
+ u64 state_start_ticks;
+ u64 *ticks_in_state; /* vector of state occurrences */
+ f64 mean_ticks_in_state;
+ f64 variance_ticks_in_state;
+ f64 total_ticks_in_state;
+} bound_track_t;
+
+bound_track_t *bound_tracks;
+
+void fatal(char *s)
+{
+ fprintf(stderr, "%s", s);
+ exit(1);
+}
+
+typedef enum {
+ PASS1=1,
+ PASS2=2,
+} pass_t;
+
+typedef struct {
+ int (*pass1)(cpel_section_header_t *, int, FILE *);
+ int (*pass2)(cpel_section_header_t *, int, FILE *);
+} section_processor_t;
+
+int bad_section(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ fprintf(ofp, "Bad (type 0) section, skipped...\n");
+ return(0);
+}
+
+int noop_pass(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ return(0);
+}
+
+int strtab_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ uword *p;
+ u8 *strtab_data_area = (u8 *)(sh+1);
+
+ /* Multiple string tables with the same name are Bad... */
+ p = hash_get_mem(the_strtab_hash, strtab_data_area);
+ if (p) {
+ fprintf(ofp, "Duplicate string table name %s", strtab_data_area);
+ }
+ /*
+ * Looks funny, but we really do want key = first string in the
+ * table, value = address(first string in the table)
+ */
+ hash_set_mem(the_strtab_hash, strtab_data_area, strtab_data_area);
+ if (verbose) {
+ fprintf(ofp, "String Table %s\n", strtab_data_area);
+ }
+ return(0);
+}
+
+int evtdef_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ int i, nevents;
+ event_definition_section_header_t *edh;
+ event_definition_t *ep;
+ u8 *this_strtab;
+ u32 event_code;
+ uword *p;
+ bound_event_t *bp;
+ int thislen;
+
+ edh = (event_definition_section_header_t *)(sh+1);
+ nevents = ntohl(edh->number_of_event_definitions);
+
+ if (verbose) {
+ fprintf(ofp, "Event Definition Section: %d definitions\n",
+ nevents);
+ }
+
+ p = hash_get_mem(the_strtab_hash, edh->string_table_name);
+ if (!p) {
+ fprintf(ofp, "Fatal: couldn't find string table\n");
+ return(1);
+ }
+ this_strtab = (u8 *)p[0];
+
+ ep = (event_definition_t *)(edh+1);
+
+ for (i = 0; i < nevents; i++) {
+ event_code = ntohl(ep->event);
+ p = hash_get(the_evtdef_hash, event_code);
+ if (p) {
+ fprintf(ofp, "Event %d redefined, retain first definition\n",
+ event_code);
+ continue;
+ }
+ vec_add2(bound_events, bp, 1);
+ bp->event_code = event_code;
+ bp->event_str = this_strtab + ntohl(ep->event_format);
+ bp->datum_str = this_strtab + ntohl(ep->datum_format);
+ bp->is_strtab_ref = 0;
+ /* Decide if the datum format is a %s format => strtab reference */
+ {
+ int j;
+ int seen_percent=0;
+
+ for (j = 0; j < strlen((char *) bp->datum_str); j++) {
+ if (bp->datum_str[j] == '%'){
+ seen_percent=1;
+ continue;
+ }
+ if (seen_percent && bp->datum_str[j] == 's') {
+ bp->is_strtab_ref = 1;
+ }
+ }
+ }
+
+ hash_set(the_evtdef_hash, event_code, bp - bound_events);
+
+ thislen = strlen((char *) bp->event_str);
+ if (thislen > widest_name_format)
+ widest_name_format = thislen;
+
+ ep++;
+ }
+ return (0);
+}
+
+int trackdef_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ int i, nevents;
+ track_definition_section_header_t *tdh;
+ track_definition_t *tp;
+ u8 *this_strtab;
+ u32 track_code;
+ uword *p;
+ bound_track_t *btp;
+ int thislen;
+ u8 *pidstr;
+ u8 *pidtid_str;
+ u8 *cp;
+ int tid, pid;
+
+ tdh = (track_definition_section_header_t *)(sh+1);
+ nevents = ntohl(tdh->number_of_track_definitions);
+
+ if (verbose) {
+ fprintf(ofp, "Track Definition Section: %d definitions\n",
+ nevents);
+ }
+
+ p = hash_get_mem(the_strtab_hash, tdh->string_table_name);
+ if (!p) {
+ fprintf(ofp, "Fatal: couldn't find string table\n");
+ return(1);
+ }
+ this_strtab = (u8 *)p[0];
+
+ tp = (track_definition_t *)(tdh+1);
+
+ for (i = 0; i < nevents; i++) {
+ track_code = ntohl(tp->track);
+ p = hash_get(the_trackdef_hash, track_code);
+ if (p) {
+ fprintf(stderr, "track %d redefined, retain first definition\n",
+ track_code);
+ continue;
+ }
+ vec_add2(bound_tracks, btp, 1);
+ btp->track = track_code;
+ btp->track_str = this_strtab + ntohl(tp->track_format);
+ hash_set(the_trackdef_hash, track_code, btp - bound_tracks);
+
+ if (verbose) {
+ fprintf(stderr, "adding track '%s'\n", btp->track_str);
+ }
+
+ thislen = strlen((char *) btp->track_str);
+ if (thislen > widest_track_format)
+ widest_track_format = thislen;
+
+ /* convert track_str "eth_server t11(20498)" to "pid:20498 tid:11" */
+ cp = btp->track_str;
+ while (*cp && *cp != '(')
+ cp++;
+ if (!*cp) {
+ fprintf(stderr, "error canonicalizing '%s'\n", btp->track_str);
+ goto out;
+ }
+ pidstr = cp+1; /* remember location of PID */
+
+ while (cp > btp->track_str && *cp != 't')
+ cp--;
+
+ if (cp == btp->track_str) {
+ fprintf(stderr, "error canonicalizing '%s'\n", btp->track_str);
+ goto out;
+ }
+ tid = atol((char *)(cp+1));
+ pid = atol((char *) pidstr);
+ pidtid_str = format(0, "pid:%d tid:%d", pid, tid);
+ vec_add1(pidtid_str, 0);
+
+ /*
+ * Note: duplicates are possible due to thread create /
+ * thread destroy operations.
+ */
+ p = hash_get_mem(the_pidtid_hash, pidtid_str);
+ if (p) {
+ vec_free(pidtid_str);
+ goto out;
+ }
+ hash_set_mem(the_pidtid_hash, pidtid_str, btp - bound_tracks);
+
+ out:
+ tp++;
+ }
+ have_trackdefs = 1;
+ return (0);
+}
+
+int unsupported_pass (cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ if (verbose) {
+ fprintf(ofp, "Unsupported type %d section\n",
+ ntohl(sh->section_type));
+ }
+ return(0);
+}
+
+int event_pass2(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ event_section_header_t *eh;
+ int nevents;
+ int i;
+ uword *p;
+ event_entry_t *ep;
+ u64 now;
+ u32 time0, time1;
+ u32 track_code;
+ u8 *this_strtab;
+ u64 ticks_in_state;
+ bound_track_t *btp;
+ bound_track_t *state_track=0;
+ u8 *pidtid_str;
+ u8 *pidtid_dup;
+ u8 *ecp;
+ u32 event_code;
+
+ eh = (event_section_header_t *)(sh+1);
+ nevents = ntohl(eh->number_of_events);
+ ticks_per_us = ((double)ntohl(eh->clock_ticks_per_second)) / 1e6;
+
+ if (verbose) {
+ fprintf(ofp, "%.3f ticks_per_us\n", ticks_per_us);
+ }
+
+ ep = (event_entry_t *)(eh+1);
+
+ p = hash_get_mem(the_strtab_hash, eh->string_table_name);
+ if (!p) {
+ fprintf(ofp, "Fatal: couldn't find string table\n");
+ return(1);
+ }
+ this_strtab = (u8 *)p[0];
+
+ /*
+ * Some logger implementation that doesn't produce
+ * trackdef sections, synthesize the bound_tracks vector
+ */
+ if (!have_trackdefs) {
+ for (i = 0; i < nevents; i++) {
+ track_code = ntohl(ep->track);
+ pidtid_dup = format(0, "%d", track_code);
+ vec_add1(pidtid_dup, 0);
+ p = hash_get_mem(the_pidtid_hash, pidtid_dup);
+ if (!p) {
+ vec_add2(bound_tracks, btp, 1);
+ btp->track = track_code;
+ btp->track_str = pidtid_dup;
+ hash_set(the_trackdef_hash, track_code, btp - bound_tracks);
+ hash_set_mem(the_pidtid_hash, pidtid_dup, btp - bound_tracks);
+ } else {
+ vec_free(pidtid_dup);
+ }
+ ep++;
+ }
+ }
+
+ ep = (event_entry_t *)(eh+1);
+
+ for (i = 0; i < nevents; i++) {
+ time0 = ntohl (ep->time[0]);
+ time1 = ntohl (ep->time[1]);
+
+ now = (((u64) time0)<<32) | time1;
+
+ event_code = ntohl(ep->event_code);
+
+ /* Find the corresponding track via the pidtid hash table */
+ if (event_code == start_event_code || event_code == end_event_code) {
+ if (have_trackdefs) {
+ pidtid_str = this_strtab + ntohl(ep->event_datum);
+ pidtid_dup = format(0, (char *) pidtid_str);
+ vec_add1(pidtid_dup, 0);
+ ecp = &pidtid_dup[vec_len(pidtid_dup)-1];
+ while (*--ecp == ' ')
+ *ecp = 0;
+ } else {
+ pidtid_dup = format(0, "%d", ntohl(ep->track));
+ vec_add1(pidtid_dup, 0);
+ }
+
+ p = hash_get_mem(the_pidtid_hash, pidtid_dup);
+ if (!p) {
+ fprintf(stderr, "warning: couldn't find '%s'\n",
+ pidtid_dup);
+ vec_free(pidtid_dup);
+ ep++;
+ continue;
+ }
+ state_track = &bound_tracks[p[0]];
+ }
+ /* Found the start-event code ? */
+ if (event_code == start_event_code) {
+ state_track->state_start_ticks = now;
+ } else if (event_code == end_event_code) {
+ /*
+ * Add a ticks-in-state record, unless
+ * e.g. the log started with the exit event
+ */
+ if (state_track->state_start_ticks) {
+ ticks_in_state = now - state_track->state_start_ticks;
+ vec_add1(state_track->ticks_in_state, ticks_in_state);
+ state_track->state_start_ticks = 0;
+ }
+ /* Otherwise, nothing */
+ }
+ ep++;
+ }
+ return(0);
+}
+
+/*
+ * Note: If necessary, add passes / columns to this table to
+ * handle section order dependencies.
+ */
+
+section_processor_t processors[CPEL_NUM_SECTION_TYPES+1] =
+{
+ {bad_section, noop_pass}, /* type 0 -- f**ked */
+ {strtab_pass1, noop_pass}, /* type 1 -- STRTAB */
+ {unsupported_pass, noop_pass}, /* type 2 -- SYMTAB */
+ {evtdef_pass1, noop_pass}, /* type 3 -- EVTDEF */
+ {trackdef_pass1, noop_pass}, /* type 4 -- TRACKDEF */
+ {noop_pass, event_pass2}, /* type 5 -- EVENTS */
+};
+
+
+int process_section(cpel_section_header_t *sh, int verbose, FILE *ofp,
+ pass_t pass)
+{
+ u32 type;
+ type = ntohl(sh->section_type);
+ int rv;
+ int (*fp)(cpel_section_header_t *, int, FILE *);
+
+ if (type > CPEL_NUM_SECTION_TYPES) {
+ fprintf(stderr, "Unknown section type %d\n", type);
+ return(1);
+ }
+ switch(pass) {
+ case PASS1:
+ fp = processors[type].pass1;
+ break;
+
+ case PASS2:
+ fp = processors[type].pass2;
+ break;
+
+ default:
+ fprintf(stderr, "Unknown pass %d\n", pass);
+ return(1);
+ }
+
+ rv = (*fp)(sh, verbose, ofp);
+
+ return(rv);
+}
+
+int cpel_dump_file_header(cpel_file_header_t *fh, int verbose, FILE *ofp)
+{
+ time_t file_time;
+
+ if (verbose) {
+ fprintf(ofp, "CPEL file: %s-endian, version %d\n",
+ ((fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) ?
+ "little" : "big"),
+ fh->endian_version & CPEL_FILE_VERSION_MASK);
+
+ file_time = ntohl(fh->file_date);
+
+ fprintf(ofp, "File created %s", ctime(&file_time));
+ fprintf(ofp, "File has %d sections\n",
+ ntohs(fh->nsections));
+ }
+
+ return(0);
+}
+
+
+int cpel_dump(u8 *cpel, int verbose, FILE *ofp)
+{
+ cpel_file_header_t *fh;
+ cpel_section_header_t *sh;
+ u16 nsections;
+ u32 section_size;
+ int i;
+
+ /* First, the file header */
+ fh = (cpel_file_header_t *)cpel;
+ if (fh->endian_version != CPEL_FILE_VERSION) {
+ if (fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) {
+ fprintf(stderr, "Little endian data format not supported\n");
+ return(1);
+ }
+ fprintf(stderr, "Unsupported file version 0x%x\n",
+ fh->endian_version);
+ return(1);
+ }
+ cpel_dump_file_header(fh, verbose, ofp);
+ nsections = ntohs(fh->nsections);
+
+ /*
+ * Take two passes through the file. PASS1 builds
+ * data structures, PASS2 actually dumps the file.
+ * Just in case the sections are in an unobvious order.
+ */
+ sh = (cpel_section_header_t *)(fh+1);
+ for (i = 0; i < nsections; i++) {
+ section_size = ntohl(sh->data_length);
+
+ if(verbose) {
+ fprintf(ofp, "Section type %d, size %d\n", ntohl(sh->section_type),
+ section_size);
+ }
+
+ if(process_section(sh, verbose, ofp, PASS1))
+ return(1);
+
+ sh++;
+ sh = (cpel_section_header_t *)(((u8 *)sh)+section_size);
+ }
+
+ sh = (cpel_section_header_t *)(fh+1);
+ for (i = 0; i < nsections; i++) {
+ if(process_section(sh, verbose, ofp, PASS2))
+ return(1);
+ section_size = ntohl(sh->data_length);
+ sh++;
+ sh = (cpel_section_header_t *)(((u8 *)sh)+section_size);
+ }
+ return(0);
+}
+
+void compute_state_statistics(int verbose, FILE *ofp)
+{
+ int i, j;
+ bound_track_t *bp;
+ f64 fticks;
+
+ /* Across the bound tracks */
+ for (i = 0; i < vec_len(bound_tracks); i++) {
+ bp = &bound_tracks[i];
+ bp->mean_ticks_in_state = 0.0;
+ bp->variance_ticks_in_state = 0.0;
+ bp->total_ticks_in_state = 0.0;
+ for (j = 0; j < vec_len(bp->ticks_in_state); j++) {
+ bp->total_ticks_in_state += (f64) bp->ticks_in_state[j];
+ }
+ /* Compute mean */
+ if (vec_len(bp->ticks_in_state)) {
+ bp->mean_ticks_in_state = bp->total_ticks_in_state /
+ ((f64) vec_len(bp->ticks_in_state));
+ }
+ /* Accumulate sum: (Xi-Xbar)**2 */
+ for (j = 0; j < vec_len(bp->ticks_in_state); j++) {
+ fticks = bp->ticks_in_state[j];
+ bp->variance_ticks_in_state +=
+ (fticks - bp->mean_ticks_in_state)*
+ (fticks - bp->mean_ticks_in_state);
+ }
+ /* Compute s**2, the unbiased estimator of sigma**2 */
+ if (vec_len(bp->ticks_in_state) > 1) {
+ bp->variance_ticks_in_state /= (f64)
+ (vec_len(bp->ticks_in_state)-1);
+ }
+ }
+}
+
+int track_compare_max (const void *arg1, const void *arg2)
+{
+ bound_track_t *a1 = (bound_track_t *)arg1;
+ bound_track_t *a2 = (bound_track_t *)arg2;
+ f64 v1, v2;
+
+ v1 = a1->total_ticks_in_state;
+ v2 = a2->total_ticks_in_state;
+
+ if (v1 < v2)
+ return (1);
+ else if (v1 == v2)
+ return (0);
+ else return (-1);
+}
+
+int track_compare_occurrences (const void *arg1, const void *arg2)
+{
+ bound_track_t *a1 = (bound_track_t *)arg1;
+ bound_track_t *a2 = (bound_track_t *)arg2;
+ f64 v1, v2;
+
+ v1 = (f64) vec_len(a1->ticks_in_state);
+ v2 = (f64) vec_len(a2->ticks_in_state);
+
+ if (v1 < v2)
+ return (1);
+ else if (v1 == v2)
+ return (0);
+ else return (-1);
+}
+
+int track_compare_name (const void *arg1, const void *arg2)
+{
+ bound_track_t *a1 = (bound_track_t *)arg1;
+ bound_track_t *a2 = (bound_track_t *)arg2;
+
+ return (strcmp((char *)(a1->track_str), (char *)(a2->track_str)));
+}
+
+void sort_state_statistics(sort_t type, FILE *ofp)
+{
+ int (*compare)(const void *, const void *);
+
+ if (summary_stats_only)
+ return;
+
+ switch(type) {
+ case SORT_MAX_TIME:
+ fprintf(ofp, "Results sorted by max time in state.\n\n");
+ compare = track_compare_max;
+ break;
+
+ case SORT_MAX_OCCURRENCES:
+ fprintf(ofp, "Results sorted by max occurrences of state.\n\n");
+ compare = track_compare_occurrences;
+ break;
+
+ case SORT_NAME:
+ compare = track_compare_name;
+ fprintf(ofp, "Results sorted by process name, thread ID, PID\n\n");
+ break;
+
+ default:
+ fatal("sort type not set?");
+ }
+
+ qsort (bound_tracks, vec_len(bound_tracks),
+ sizeof (bound_track_t), compare);
+}
+
+void print_state_statistics(int verbose, FILE *ofp)
+{
+ int i,j;
+ u8 *trackpad;
+ bound_track_t *bp;
+ f64 total_time = 0.0;
+ f64 total_switches = 0.0;
+
+ trackpad = format(0, "%%-%ds ", widest_track_format);
+ vec_add1(trackpad, 0);
+
+ if (!summary_stats_only) {
+ fprintf(ofp, (char *)trackpad, "ProcName Thread(PID)");
+ fprintf(ofp, " Mean(us) Stdev(us) Total(us) N\n");
+ }
+
+ for (i = 0; i < vec_len(bound_tracks); i++) {
+ bp = &bound_tracks[i];
+ if (bp->mean_ticks_in_state == 0.0)
+ continue;
+
+ if (name_filter &&
+ strncmp((char *)bp->track_str, (char *)name_filter,
+ strlen((char *)name_filter)))
+ continue;
+
+ /*
+ * Exclude kernel threads (e.g. idle thread) from
+ * state statistics
+ */
+ if (exclude_kernel_from_summary_stats &&
+ !strncmp((char *) bp->track_str, "kernel ", 7))
+ continue;
+
+ total_switches += (f64) vec_len(bp->ticks_in_state);
+
+ if (!summary_stats_only) {
+ fprintf(ofp, (char *) trackpad, bp->track_str);
+ fprintf(ofp, "%10.3f +- %10.3f",
+ bp->mean_ticks_in_state / ticks_per_us,
+ sqrt(bp->variance_ticks_in_state)
+ / ticks_per_us);
+ fprintf(ofp, "%12.3f",
+ bp->total_ticks_in_state / ticks_per_us);
+ fprintf(ofp, "%8d\n", vec_len(bp->ticks_in_state));
+ }
+
+ if (scatterplot) {
+ for (j = 0; j < vec_len(bp->ticks_in_state); j++) {
+ fprintf(ofp, "%.3f\n",
+ (f64)bp->ticks_in_state[j] / ticks_per_us);
+ }
+ }
+
+ total_time += bp->total_ticks_in_state;
+ }
+
+ if (!summary_stats_only)
+ fprintf(ofp, "\n");
+ fprintf(ofp, "Note: the following statistics %s kernel-thread activity.\n",
+ exclude_kernel_from_summary_stats ? "exclude" : "include");
+ if (name_filter)
+ fprintf(ofp,
+ "Note: only pid/proc/threads matching '%s' are included.\n",
+ name_filter);
+
+ fprintf(ofp,
+ "Total time in state: %10.3f (us), Total state occurrences: %.0f\n",
+ total_time / ticks_per_us, total_switches);
+ fprintf(ofp, "Average time in state: %10.3f (us)\n",
+ (total_time / total_switches) / ticks_per_us);
+ fprintf(ofp, "State start event: %d, state end event: %d\n",
+ start_event_code, end_event_code);
+}
+
+char *mapfile (char *file)
+{
+ struct stat statb;
+ char *rv;
+ int maphfile;
+ size_t mapfsize;
+
+ maphfile = open (file, O_RDONLY);
+
+ if (maphfile < 0)
+ {
+ fprintf (stderr, "Couldn't read %s, skipping it...\n", file);
+ return (NULL);
+ }
+
+ if (fstat (maphfile, &statb) < 0)
+ {
+ fprintf (stderr, "Couldn't get size of %s, skipping it...\n", file);
+ return (NULL);
+ }
+
+ /* Don't try to mmap directories, FIFOs, semaphores, etc. */
+ if (! (statb.st_mode & S_IFREG)) {
+ fprintf (stderr, "%s is not a regular file, skipping it...\n", file);
+ return (NULL);
+ }
+
+ mapfsize = statb.st_size;
+
+ if (mapfsize < 3)
+ {
+ fprintf (stderr, "%s zero-length, skipping it...\n", file);
+ close (maphfile);
+ return (NULL);
+ }
+
+ rv = mmap (0, mapfsize, PROT_READ, MAP_SHARED, maphfile, 0);
+
+ if (rv == 0)
+ {
+ fprintf (stderr, "%s problem mapping, I quit...\n", file);
+ exit (-1);
+ }
+ close (maphfile);
+ return (rv);
+}
+
+/*
+ * main
+ */
+int main (int argc, char **argv)
+{
+ char *cpel_file = 0;
+ char *outputfile = 0;
+ FILE *ofp;
+ char *cpel;
+ int verbose=0;
+ int curarg=1;
+
+ while (curarg < argc) {
+ if (!strncmp(argv[curarg], "--input-file", 3)) {
+ curarg++;
+ if (curarg < argc) {
+ cpel_file = argv[curarg];
+ curarg++;
+ continue;
+ }
+ fatal("Missing filename after --input-file\n");
+ }
+ if (!strncmp(argv[curarg], "--output-file", 3)) {
+ curarg ++;
+ if (curarg < argc) {
+ outputfile = argv[curarg];
+ curarg ++;
+ continue;
+ }
+ fatal("Missing filename after --output-file\n");
+ }
+ if (!strncmp(argv[curarg], "--verbose", 3)) {
+ curarg++;
+ verbose++;
+ continue;
+ }
+ if (!strncmp(argv[curarg], "--scatterplot", 4)) {
+ curarg++;
+ scatterplot=1;
+ continue;
+ }
+
+ if (!strncmp(argv[curarg], "--start-event", 4)) {
+ curarg++;
+ if (curarg < argc) {
+ start_event_code = atol(argv[curarg]);
+ curarg ++;
+ continue;
+ }
+ fatal("Missing integer after --start-event\n");
+ }
+ if (!strncmp(argv[curarg], "--end-event", 4)) {
+ curarg++;
+ if (curarg < argc) {
+ end_event_code = atol(argv[curarg]);
+ curarg ++;
+ continue;
+ }
+ fatal("Missing integer after --end-event\n");
+ }
+ if (!strncmp(argv[curarg], "--max-time-sort", 7)) {
+ sort_type = SORT_MAX_TIME;
+ curarg++;
+ continue;
+ }
+ if (!strncmp(argv[curarg], "--max-occurrence-sort", 7)) {
+ sort_type = SORT_MAX_OCCURRENCES;
+ curarg++;
+ continue;
+ }
+ if (!strncmp(argv[curarg], "--name-sort", 3)) {
+ sort_type = SORT_NAME;
+ curarg++;
+ continue;
+ }
+ if (!strncmp(argv[curarg], "--kernel-included", 3)) {
+ exclude_kernel_from_summary_stats = 0;
+ curarg++;
+ continue;
+ }
+ if (!strncmp(argv[curarg], "--summary", 3)) {
+ summary_stats_only=1;
+ curarg++;
+ continue;
+ }
+ if (!strncmp(argv[curarg], "--filter", 3)) {
+ curarg ++;
+ if (curarg < argc) {
+ name_filter = (u8 *) argv[curarg];
+ curarg ++;
+ continue;
+ }
+ fatal("Missing filter string after --filter\n");
+ }
+
+
+ usage:
+ fprintf(stderr,
+ "cpelatency --input-file <filename> [--output-file <filename>]\n");
+ fprintf(stderr,
+ " [--start-event <decimal>] [--verbose]\n");
+ fprintf(stderr,
+ " [--end-event <decimal>]\n");
+ fprintf(stderr,
+ " [--max-time-sort(default) | --max-occurrence-sort |\n");
+
+ fprintf(stderr,
+ " --name-sort-sort] [--kernel-included]\n");
+
+ fprintf(stderr,
+ " [--summary-stats-only] [--scatterplot]\n");
+
+ fprintf(stderr, "%s\n", version);
+ exit(1);
+ }
+
+ if (cpel_file == 0)
+ goto usage;
+
+ cpel = mapfile(cpel_file);
+ if (cpel == 0) {
+ fprintf(stderr, "Couldn't map %s...\n", cpel_file);
+ exit(1);
+ }
+
+ if (!outputfile) {
+ ofp = fdopen(1, "w");
+ if (ofp == NULL) {
+ fprintf(stderr, "Couldn't fdopen(1)?\n");
+ exit(1);
+ }
+ } else {
+ ofp = fopen(outputfile, "w");
+ if (ofp == NULL) {
+ fprintf(stderr, "Couldn't create %s...\n", outputfile);
+ exit(1);
+ }
+ }
+
+ the_strtab_hash = hash_create_string (0, sizeof (uword));
+ the_evtdef_hash = hash_create (0, sizeof (uword));
+ the_trackdef_hash = hash_create (0, sizeof (uword));
+ the_pidtid_hash = hash_create_string (0, sizeof(uword));
+
+ if (cpel_dump((u8 *)cpel, verbose, ofp)) {
+ if (outputfile)
+ unlink(outputfile);
+ }
+
+ compute_state_statistics(verbose, ofp);
+ sort_state_statistics(sort_type, ofp);
+ print_state_statistics(verbose, ofp);
+
+ fclose(ofp);
+ return(0);
+}
diff --git a/perftool/cpeldump.c b/perftool/cpeldump.c
new file mode 100644
index 00000000000..9011bd039ec
--- /dev/null
+++ b/perftool/cpeldump.c
@@ -0,0 +1,638 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2006-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <vppinfra/clib.h>
+#include <vppinfra/vec.h>
+#include <vppinfra/hash.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <time.h>
+#include "cpel.h"
+
+char *time_format = "%.03d:%.02d:%.02d:%.03d:%.03d ";
+static char version[] = "cpeldump 2.0";
+
+#define USEC_PER_MS 1000LL
+#define USEC_PER_SECOND (1000*USEC_PER_MS)
+#define USEC_PER_MINUTE (60*USEC_PER_SECOND)
+#define USEC_PER_HOUR (60*USEC_PER_MINUTE)
+
+uword *the_strtab_hash; /* (name, base-VA) hash of all string tables */
+uword *the_evtdef_hash; /* (event-id, event-definition) hash */
+uword *the_trackdef_hash; /* (track-id, track-definition) hash */
+
+int widest_name_format=5;
+int widest_track_format=5;
+
+typedef struct bound_event_ {
+ u32 event_code;
+ u8 *event_str;
+ u8 *datum_str;
+ u32 is_strtab_ref;
+} bound_event_t;
+
+bound_event_t *bound_events;
+
+typedef struct bound_track_ {
+ u32 track;
+ u8 *track_str;
+} bound_track_t;
+
+bound_track_t *bound_tracks;
+
+void fatal(char *s)
+{
+ fprintf(stderr, "%s", s);
+ exit(1);
+}
+
+typedef enum {
+ PASS1=1,
+ PASS2=2,
+} pass_t;
+
+typedef struct {
+ int (*pass1)(cpel_section_header_t *, int, FILE *);
+ int (*pass2)(cpel_section_header_t *, int, FILE *);
+} section_processor_t;
+
+int bad_section(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ fprintf(ofp, "Bad (type 0) section, skipped...\n");
+ return(0);
+}
+
+int noop_pass(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ return(0);
+}
+
+int strtab_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ uword *p;
+ u8 *strtab_data_area = (u8 *)(sh+1);
+
+ /* Multiple string tables with the same name are Bad... */
+ p = hash_get_mem(the_strtab_hash, strtab_data_area);
+ if (p) {
+ fprintf(ofp, "Duplicate string table name %s", strtab_data_area);
+ }
+ /*
+ * Looks funny, but we really do want key = first string in the
+ * table, value = address(first string in the table)
+ */
+ hash_set_mem(the_strtab_hash, strtab_data_area, strtab_data_area);
+ if (verbose) {
+ fprintf(stderr, "String Table %s\n", strtab_data_area);
+ }
+ return(0);
+}
+
+int evtdef_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ int i, nevents;
+ event_definition_section_header_t *edh;
+ event_definition_t *ep;
+ u8 *this_strtab;
+ u32 event_code;
+ uword *p;
+ bound_event_t *bp;
+ int thislen;
+
+ edh = (event_definition_section_header_t *)(sh+1);
+ nevents = ntohl(edh->number_of_event_definitions);
+
+ if (verbose) {
+ fprintf(stderr, "Event Definition Section: %d definitions\n",
+ nevents);
+ }
+
+ p = hash_get_mem(the_strtab_hash, edh->string_table_name);
+ if (!p) {
+ fprintf(ofp, "Fatal: couldn't find string table\n");
+ return(1);
+ }
+ this_strtab = (u8 *)p[0];
+
+ ep = (event_definition_t *)(edh+1);
+
+ for (i = 0; i < nevents; i++) {
+ event_code = ntohl(ep->event);
+ p = hash_get(the_evtdef_hash, event_code);
+ if (p) {
+ fprintf(ofp, "Event %d redefined, retain first definition\n",
+ event_code);
+ continue;
+ }
+ vec_add2(bound_events, bp, 1);
+ bp->event_code = event_code;
+ bp->event_str = this_strtab + ntohl(ep->event_format);
+ bp->datum_str = this_strtab + ntohl(ep->datum_format);
+ bp->is_strtab_ref = 0;
+ /* Decide if the datum format is a %s format => strtab reference */
+ {
+ int j;
+ int seen_percent=0;
+
+ for (j = 0; j < strlen((char *)bp->datum_str); j++) {
+ if (bp->datum_str[j] == '%'){
+ seen_percent=1;
+ continue;
+ }
+ if (seen_percent && bp->datum_str[j] == 's') {
+ bp->is_strtab_ref = 1;
+ }
+ }
+ }
+
+ hash_set(the_evtdef_hash, event_code, bp - bound_events);
+
+ thislen = strlen((char *)bp->event_str);
+ if (thislen > widest_name_format)
+ widest_name_format = thislen;
+
+ ep++;
+ }
+ return (0);
+}
+
+int trackdef_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ int i, nevents;
+ track_definition_section_header_t *tdh;
+ track_definition_t *tp;
+ u8 *this_strtab;
+ u32 track_code;
+ uword *p;
+ bound_track_t *btp;
+ int thislen;
+
+ tdh = (track_definition_section_header_t *)(sh+1);
+ nevents = ntohl(tdh->number_of_track_definitions);
+
+ if (verbose) {
+ fprintf(stderr, "Track Definition Section: %d definitions\n",
+ nevents);
+ }
+
+ p = hash_get_mem(the_strtab_hash, tdh->string_table_name);
+ if (!p) {
+ fprintf(ofp, "Fatal: couldn't find string table\n");
+ return(1);
+ }
+ this_strtab = (u8 *)p[0];
+
+ tp = (track_definition_t *)(tdh+1);
+
+ for (i = 0; i < nevents; i++) {
+ track_code = ntohl(tp->track);
+ p = hash_get(the_trackdef_hash, track_code);
+ if (p) {
+ fprintf(ofp, "track %d redefined, retain first definition\n",
+ track_code);
+ continue;
+ }
+ vec_add2(bound_tracks, btp, 1);
+ btp->track = track_code;
+ btp->track_str = this_strtab + ntohl(tp->track_format);
+ hash_set(the_trackdef_hash, track_code, btp - bound_tracks);
+
+ thislen = strlen((char *)btp->track_str);
+ if (thislen > widest_track_format)
+ widest_track_format = thislen;
+ tp++;
+ }
+ return (0);
+}
+
+int unsupported_pass (cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ if (verbose) {
+ fprintf(stderr, "Unsupported type %d section\n",
+ ntohl(sh->section_type));
+ }
+ return(0);
+}
+
+int event_pass2(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ event_section_header_t *eh;
+ f64 ticks_per_us;
+ u32 event_code, track_code;
+ u64 starttime = 0xFFFFFFFFFFFFFFFFULL;
+ int nevents;
+ int i;
+ uword *p;
+ event_entry_t *ep;
+ u64 now;
+ u64 delta;
+ u32 hours, minutes, seconds, msec, usec;
+ u32 time0, time1;
+ double d;
+ bound_event_t *bp;
+ bound_event_t generic_event;
+ bound_track_t *tp=0;
+ bound_track_t generic_track;
+ u32 last_track_code;
+ u8 *s, *evtpad, *trackpad;
+ u8 *this_strtab;
+
+ generic_event.event_str = (u8 *)"%d";
+ generic_event.datum_str = (u8 *)"0x%08x";
+ generic_event.is_strtab_ref = 0;
+
+ generic_track.track_str = (u8 *)"%d";
+ last_track_code = 0xdeadbeef;
+
+ eh = (event_section_header_t *)(sh+1);
+ nevents = ntohl(eh->number_of_events);
+ ticks_per_us = ((double)ntohl(eh->clock_ticks_per_second)) / 1e6;
+
+ if (verbose) {
+ fprintf(stderr, "Event section: %d events, %.3f ticks_per_us\n",
+ nevents, ticks_per_us);
+ }
+
+ ep = (event_entry_t *)(eh+1);
+
+ p = hash_get_mem(the_strtab_hash, eh->string_table_name);
+ if (!p) {
+ fprintf(ofp, "Fatal: couldn't find string table\n");
+ return(1);
+ }
+ this_strtab = (u8 *)p[0];
+
+ evtpad = format(0, "%%-%ds ", widest_name_format);
+ vec_add1(evtpad, 0);
+ trackpad = format(0, "%%-%ds ", widest_track_format);
+ vec_add1(trackpad, 0);
+
+ for (i = 0; i < nevents; i++) {
+ time0 = ntohl (ep->time[0]);
+ time1 = ntohl (ep->time[1]);
+
+ now = (((u64) time0)<<32) | time1;
+
+ /* Convert from bus ticks to usec */
+ d = now;
+ d /= ticks_per_us;
+
+ now = d;
+
+ if (starttime == 0xFFFFFFFFFFFFFFFFULL)
+ starttime = now;
+
+ delta = now - starttime;
+
+ /* Delta = time since first event, in usec */
+
+ hours = delta / USEC_PER_HOUR;
+ if (hours)
+ delta -= ((u64) hours * USEC_PER_HOUR);
+ minutes = delta / USEC_PER_MINUTE;
+ if (minutes)
+ delta -= ((u64) minutes * USEC_PER_MINUTE);
+ seconds = delta / USEC_PER_SECOND;
+ if (seconds)
+ delta -= ((u64) seconds * USEC_PER_SECOND);
+ msec = delta / USEC_PER_MS;
+ if (msec)
+ delta -= ((u64) msec * USEC_PER_MS);
+
+ usec = delta;
+
+ /* Output the timestamp */
+ fprintf(ofp, time_format, hours, minutes, seconds, msec, usec);
+
+ /* output the track */
+ track_code = ntohl(ep->track);
+
+ if (track_code != last_track_code) {
+ p = hash_get(the_trackdef_hash, track_code);
+ if (p) {
+ tp = &bound_tracks[p[0]];
+ } else {
+ tp = &generic_track;
+ }
+ }
+ s = format(0, (char *)tp->track_str, track_code);
+ vec_add1(s, 0);
+ fprintf(ofp, (char *)trackpad, s);
+ vec_free(s);
+
+ /* output the event and datum */
+ if (0 && verbose) {
+ fprintf(stderr, "raw event code %d, raw event datum 0x%x\n",
+ ntohl(ep->event_code), ntohl(ep->event_datum));
+ }
+
+ event_code = ntohl(ep->event_code);
+ p = hash_get(the_evtdef_hash, event_code);
+ if (p) {
+ bp = &bound_events[p[0]];
+ } else {
+ bp = &generic_event;
+ }
+ s = format(0, (char *)bp->event_str, ntohl(ep->event_code));
+ vec_add1(s, 0);
+ fprintf(ofp, (char *)evtpad, s);
+ vec_free(s);
+ if (bp->is_strtab_ref) {
+ fprintf(ofp, (char *) bp->datum_str,
+ &this_strtab[ntohl(ep->event_datum)]);
+ } else {
+ fprintf(ofp, (char *) bp->datum_str, ntohl(ep->event_datum));
+ }
+ fputs("\n", ofp);
+ ep++;
+ }
+ vec_free(evtpad);
+ vec_free(trackpad);
+ return(0);
+}
+
+/*
+ * Note: If necessary, add passes / columns to this table to
+ * handle section order dependencies.
+ */
+
+section_processor_t processors[CPEL_NUM_SECTION_TYPES+1] =
+{
+ {bad_section, noop_pass}, /* type 0 -- f**ked */
+ {strtab_pass1, noop_pass}, /* type 1 -- STRTAB */
+ {unsupported_pass, noop_pass}, /* type 2 -- SYMTAB */
+ {evtdef_pass1, noop_pass}, /* type 3 -- EVTDEF */
+ {trackdef_pass1, noop_pass}, /* type 4 -- TRACKDEF */
+ {noop_pass, event_pass2}, /* type 5 -- EVENTS */
+};
+
+
+int process_section(cpel_section_header_t *sh, int verbose, FILE *ofp,
+ pass_t pass)
+{
+ u32 type;
+ type = ntohl(sh->section_type);
+ int rv;
+ int (*fp)(cpel_section_header_t *, int, FILE *);
+
+ if (type > CPEL_NUM_SECTION_TYPES) {
+ fprintf(stderr, "Unknown section type %d\n", type);
+ return(1);
+ }
+ switch(pass) {
+ case PASS1:
+ fp = processors[type].pass1;
+ break;
+
+ case PASS2:
+ fp = processors[type].pass2;
+ break;
+
+ default:
+ fprintf(stderr, "Unknown pass %d\n", pass);
+ return(1);
+ }
+
+ rv = (*fp)(sh, verbose, ofp);
+
+ return(rv);
+}
+
+int cpel_dump_file_header(cpel_file_header_t *fh, int verbose, FILE *ofp)
+{
+ time_t file_time;
+
+ if (verbose) {
+ fprintf(stderr, "CPEL file: %s-endian, version %d\n",
+ ((fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) ?
+ "little" : "big"),
+ fh->endian_version & CPEL_FILE_VERSION_MASK);
+
+ file_time = ntohl(fh->file_date);
+
+ fprintf(stderr, "File created %s", ctime(&file_time));
+ fprintf(stderr, "File has %d sections\n",
+ ntohs(fh->nsections));
+ }
+
+ return(0);
+}
+
+
+int cpel_dump(u8 *cpel, int verbose, FILE *ofp)
+{
+ cpel_file_header_t *fh;
+ cpel_section_header_t *sh;
+ u16 nsections;
+ u32 section_size;
+ int i;
+
+ /* First, the file header */
+ fh = (cpel_file_header_t *)cpel;
+ if (fh->endian_version != CPEL_FILE_VERSION) {
+ if (fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) {
+ fprintf(stderr, "Little endian data format not supported\n");
+ return(1);
+ }
+ fprintf(stderr, "Unsupported file version 0x%x\n",
+ fh->endian_version);
+ return(1);
+ }
+ cpel_dump_file_header(fh, verbose, ofp);
+ nsections = ntohs(fh->nsections);
+
+ /*
+ * Take two passes through the file. PASS1 builds
+ * data structures, PASS2 actually dumps the file.
+ * Just in case the sections are in an unobvious order.
+ */
+ sh = (cpel_section_header_t *)(fh+1);
+ for (i = 0; i < nsections; i++) {
+ section_size = ntohl(sh->data_length);
+
+ if(verbose) {
+ fprintf(stderr,
+ "Section type %d, size %d\n", ntohl(sh->section_type),
+ section_size);
+ }
+
+ if(process_section(sh, verbose, ofp, PASS1))
+ return(1);
+
+ sh++;
+ sh = (cpel_section_header_t *)(((u8 *)sh)+section_size);
+ }
+
+ sh = (cpel_section_header_t *)(fh+1);
+ for (i = 0; i < nsections; i++) {
+ if(process_section(sh, verbose, ofp, PASS2))
+ return(1);
+ section_size = ntohl(sh->data_length);
+ sh++;
+ sh = (cpel_section_header_t *)(((u8 *)sh)+section_size);
+ }
+ return(0);
+}
+
+
+char *mapfile (char *file)
+{
+ struct stat statb;
+ char *rv;
+ int maphfile;
+ size_t mapfsize;
+
+ maphfile = open (file, O_RDONLY);
+
+ if (maphfile < 0)
+ {
+ fprintf (stderr, "Couldn't read %s, skipping it...\n", file);
+ return (NULL);
+ }
+
+ if (fstat (maphfile, &statb) < 0)
+ {
+ fprintf (stderr, "Couldn't get size of %s, skipping it...\n", file);
+ return (NULL);
+ }
+
+ /* Don't try to mmap directories, FIFOs, semaphores, etc. */
+ if (! (statb.st_mode & S_IFREG)) {
+ fprintf (stderr, "%s is not a regular file, skipping it...\n", file);
+ return (NULL);
+ }
+
+ mapfsize = statb.st_size;
+
+ if (mapfsize < 3)
+ {
+ fprintf (stderr, "%s zero-length, skipping it...\n", file);
+ close (maphfile);
+ return (NULL);
+ }
+
+ rv = mmap (0, mapfsize, PROT_READ, MAP_SHARED, maphfile, 0);
+
+ if (rv == 0)
+ {
+ fprintf (stderr, "%s problem mapping, I quit...\n", file);
+ exit (-1);
+ }
+ close (maphfile);
+ return (rv);
+}
+
+/*
+ * main
+ */
+int main (int argc, char **argv)
+{
+ char *cpel_file = 0;
+ char *outputfile = 0;
+ FILE *ofp;
+ char *cpel;
+ int verbose=0;
+ int curarg=1;
+
+ while (curarg < argc) {
+ if (!strncmp(argv[curarg], "--input-file", 3)) {
+ curarg++;
+ if (curarg < argc) {
+ cpel_file = argv[curarg];
+ curarg++;
+ continue;
+ }
+ fatal("Missing filename after --input-file\n");
+ }
+ if (!strncmp(argv[curarg], "--output-file", 3)) {
+ curarg ++;
+ if (curarg < argc) {
+ outputfile = argv[curarg];
+ curarg ++;
+ continue;
+ }
+ fatal("Missing filename after --output-file\n");
+ }
+ if (!strncmp(argv[curarg], "--verbose", 3)) {
+ curarg++;
+ verbose = 1;
+ continue;
+ }
+
+ usage:
+ fprintf(stderr,
+ "cpeldump --input-file <filename> [--output-file <filename>]\n");
+ fprintf(stderr, "%s\n", version);
+ exit(1);
+ }
+
+ if (cpel_file == 0)
+ goto usage;
+
+ cpel = mapfile(cpel_file);
+ if (cpel == 0) {
+ fprintf(stderr, "Couldn't map %s...\n", cpel_file);
+ exit(1);
+ }
+
+ if (!outputfile) {
+ ofp = fdopen(1, "w");
+ if (ofp == NULL) {
+ fprintf(stderr, "Couldn't fdopen(1)?\n");
+ exit(1);
+ }
+ } else {
+ ofp = fopen(outputfile, "w");
+ if (ofp == NULL) {
+ fprintf(stderr, "Couldn't create %s...\n", outputfile);
+ exit(1);
+ }
+ }
+
+ the_strtab_hash = hash_create_string (0, sizeof (uword));
+ the_evtdef_hash = hash_create (0, sizeof (uword));
+ the_trackdef_hash = hash_create (0, sizeof (uword));
+
+#ifdef TEST_TRACK_INFO
+ {
+ bound_track_t *btp;
+ vec_add2(bound_tracks, btp, 1);
+ btp->track = 0;
+ btp->track_str = "cpu %d";
+ hash_set(the_trackdef_hash, 0, btp - bound_tracks);
+ hash_set(the_trackdef_hash, 1, btp - bound_tracks);
+ }
+#endif
+
+ if (cpel_dump((u8 *)cpel, verbose, ofp)) {
+ if (outputfile)
+ unlink(outputfile);
+ }
+
+ fclose(ofp);
+ return(0);
+}
diff --git a/perftool/cpelinreg.c b/perftool/cpelinreg.c
new file mode 100644
index 00000000000..115afad7fb2
--- /dev/null
+++ b/perftool/cpelinreg.c
@@ -0,0 +1,892 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2008-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Search for O(N**2) functions bracketed by before/after
+ * events. The "before" event's datum is used as a tag, e.g. which function
+ * did we call that's strongly O(N).
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <vppinfra/clib.h>
+#include <vppinfra/vec.h>
+#include <vppinfra/hash.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <time.h>
+#include "cpel.h"
+
+FILE *g_ifp;
+char *g_ifile;
+
+typedef unsigned long long ulonglong;
+
+void process_traces (void);
+void record_instance (ulong tag, ulonglong time);
+void report_actors (void);
+void scatterplot_data(void);
+int entry_event, exit_event;
+int nokey;
+char *version = "cpelinreg 2.0";
+int model_these[10];
+int model_index;
+int summary_stats;
+ulonglong first_start_time;
+ulonglong last_end_time;
+ulonglong total_time;
+ulong scatterkey;
+int inline_mokus;
+
+typedef struct bound_track_ {
+ u32 track_code;
+ u32 *start_datum;
+ u8 *dup_event;
+ int state;
+ u64 *start_time;
+ u64 thread_timestamp;
+ u64 time_thread_on_cpu;
+} bound_track_t;
+
+bound_track_t *bound_tracks;
+uword *the_trackdef_hash;
+
+
+#define MAXSTACK 128
+
+typedef struct instance_ {
+ struct instance_ *next;
+ ulonglong time;
+}instance_t;
+
+typedef struct actor_ {
+ struct actor_ *next;
+ ulong key;
+ struct instance_ *first;
+ struct instance_ *last;
+ double a;
+ double b;
+ double min;
+ double max;
+ double mean;
+ double r;
+ ulong ninst;
+} actor_t;
+
+#define NBUCKETS 1811
+
+actor_t *hash[NBUCKETS];
+
+actor_t *find_or_create_actor (ulong key)
+{
+ ulong bucket;
+ actor_t *ap;
+ u8 *mem;
+
+ bucket = key % NBUCKETS;
+
+ ap = hash[bucket];
+
+ if (ap == NULL) {
+ /* Ensure 8-byte alignment to avoid (double) alignment faults */
+ mem = malloc(sizeof(*ap) + 4);
+ if (((uword)(mem)) & 0x7)
+ mem += 4;
+ ap = (actor_t *)mem;
+
+ if (ap == NULL) {
+ fprintf (stderr, "out of memory...\n");
+ exit (1);
+ }
+ ap->next = 0;
+ ap->key = key;
+ ap->first = 0;
+ ap->last = 0;
+ ap->a = 0.00;
+ ap->b = 0.00;
+ hash [bucket] = ap;
+ return (ap);
+ }
+
+ while (ap) {
+ if (ap->key == key)
+ return (ap);
+ ap = ap->next;
+ }
+
+ mem = malloc(sizeof(*ap)+4);
+ if (((uword)(mem) & 0x7))
+ mem += 4;
+ ap = (actor_t *)mem;
+
+ if (ap == NULL) {
+ fprintf (stderr, "out of memory...\n");
+ exit (1);
+ }
+ ap->key = key;
+ ap->first = 0;
+ ap->last = 0;
+ ap->a = 0.00;
+ ap->b = 0.00;
+
+ ap->next = hash[bucket];
+ hash[bucket] = ap;
+
+ return (ap);
+}
+
+void record_instance (ulong key, ulonglong time)
+{
+ actor_t *ap;
+ instance_t *ip;
+
+ if (nokey)
+ key = 0;
+
+ ap = find_or_create_actor (key);
+
+ ip = (instance_t *)malloc(sizeof(*ip));
+ if (ip == NULL) {
+ fprintf (stderr, "out of memory...\n");
+ exit (1);
+ }
+ ip->time = time;
+ ip->next = 0;
+
+ if (ap->first == 0) {
+ ap->first = ip;
+ ap->last = ip;
+ ap->ninst = 1;
+ } else {
+ ap->last->next = ip;
+ ap->last = ip;
+ ap->ninst++;
+ }
+}
+
+#define NINSTANCE 200000
+
+double x[NINSTANCE];
+double y[NINSTANCE];
+
+int actor_compare (const void *arg1, const void *arg2)
+{
+ double e10k1, e10k2;
+ actor_t **a1 = (actor_t **)arg1;
+ actor_t **a2 = (actor_t **)arg2;
+ double ninst1, ninst2;
+
+ ninst1 = ((double)((*a1)->ninst));
+ ninst2 = ((double)((*a2)->ninst));
+
+ e10k1 = ninst1 * ((*a1)->mean);
+ e10k2 = ninst2 * ((*a2)->mean);
+
+ if (e10k1 < e10k2)
+ return (1);
+ else if (e10k1 == e10k2)
+ return (0);
+ else
+ return (-1);
+}
+
+void report_actors (void)
+{
+ int i;
+ actor_t *ap;
+ instance_t *ip;
+ int nactors = 0;
+ int ninstance;
+ actor_t **actor_vector;
+ double e10k;
+ extern void linreg (double *x, double *y, int nitems, double *a, double *b,
+ double *minp, double *maxp, double *meanp, double *r);
+
+ for (i = 0; i < NBUCKETS; i++) {
+ ap = hash[i];
+ if (ap == NULL)
+ continue;
+ while (ap) {
+ nactors++;
+ ninstance = 0;
+
+ ip = ap->first;
+
+ while (ip) {
+ if (ninstance < NINSTANCE) {
+ x[ninstance] = ninstance;
+ y[ninstance] = ((double)ip->time);
+ ninstance++;
+ }
+ ip = ip->next;
+ }
+ if (ninstance > 1) {
+#if DEBUG > 0
+ int j;
+
+ for (j = 0; j < ninstance; j++) {
+ printf("x[%d] = %10.2f, y[%d] = %10.2f\n",
+ j, x[j], j, y[j]);
+ }
+#endif
+
+ linreg (x, y, ninstance, &ap->a, &ap->b, &ap->min,
+ &ap->max, &ap->mean, &ap->r);
+ } else {
+ ap->a = 0.00;
+ ap->b = 0.00;
+ }
+
+ ap = ap->next;
+ }
+ }
+
+ actor_vector = (actor_t **)malloc (nactors*sizeof(*actor_vector));
+ nactors = 0;
+
+ for (i = 0; i < NBUCKETS; i++) {
+ ap = hash[i];
+ if (ap == NULL)
+ continue;
+ while (ap) {
+ if ((ap->a != 0.00) || (ap->b != 0.00)) {
+ actor_vector[nactors++] = ap;
+ }
+ ap = ap->next;
+ }
+ }
+
+ qsort (actor_vector, nactors, sizeof (actor_t *), actor_compare);
+
+ if (summary_stats)
+ printf("NInst Offset Slope T(Ninst) Min Max Avg %%InstTime R Key");
+ else
+ printf("NInst Offset Slope T(Ninst) Key");
+
+ for (i = 0; i < model_index; i++) {
+ printf ("T @ %-8d ", model_these[i]);
+ }
+
+ printf ("\n");
+
+ for (i = 0; i < nactors; i++) {
+ int j;
+ double ninst;
+ double pcttot;
+ ap = actor_vector[i];
+ ninst = ap->ninst;
+
+ e10k = ninst * (ap->a + ap->b*((ninst-1.0)/2.0));
+
+ if (ap->ninst) {
+ if (summary_stats) {
+ pcttot = (e10k / ((double)total_time)) * 100.0;
+ printf ("%6ld %11.2f %11.2f %11.2f %11.2f %11.2f %11.2f %11.2f %11.2f 0x%08lx ",
+ ap->ninst, ap->a, ap->b, e10k, ap->min,
+ ap->max, ap->mean, pcttot, ap->r, ap->key);
+ }
+ else
+ printf ("%6ld %11.2f %11.2f %11.2f 0x%08lx ",
+ ap->ninst, ap->a, ap->b, e10k, ap->key);
+
+ for (j = 0; j < model_index; j++) {
+ ninst = model_these[j];
+ e10k = ninst * (ap->a + ap->b*((ninst-1.0)/2.0));
+ printf ("%10.2f ", e10k);
+ }
+ printf ("\n");
+ }
+ }
+}
+
+void scatterplot_data(void)
+{
+ actor_t *ap;
+ int i;
+ instance_t *ip;
+ double time;
+ int count=0;
+
+ for (i = 0; i < NBUCKETS; i++) {
+ ap = hash[i];
+ if (ap == NULL)
+ continue;
+ while (ap) {
+ if (ap->key == scatterkey){
+ ip = ap->first;
+ while (ip) {
+ time = ((double)ip->time);
+ printf ("%d\t%.0f\n", count++, time);
+ ip = ip->next;
+ }
+ return;
+ }
+ ap = ap->next;
+ }
+ }
+}
+
+
+void fatal(char *s)
+{
+ fprintf(stderr, "%s", s);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+typedef enum {
+ PASS1=1,
+} pass_t;
+
+typedef struct {
+ int (*pass1)(cpel_section_header_t *, int, FILE *);
+} section_processor_t;
+
+int bad_section(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ fprintf(ofp, "Bad (type 0) section, skipped...\n");
+ return(0);
+}
+
+int noop_pass(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ return(0);
+}
+
+int unsupported_pass (cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ if (verbose) {
+ fprintf(ofp, "Unsupported type %d section\n",
+ ntohl(sh->section_type));
+ }
+ return(0);
+}
+
+int trackdef_pass(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ int i, nevents;
+ track_definition_section_header_t *tdh;
+ track_definition_t *tp;
+ u32 track_code;
+ uword *p;
+ bound_track_t *btp;
+
+ tdh = (track_definition_section_header_t *)(sh+1);
+ nevents = ntohl(tdh->number_of_track_definitions);
+
+ if (verbose) {
+ fprintf(stderr, "Track Definition Section: %d definitions\n",
+ nevents);
+ }
+
+ tp = (track_definition_t *)(tdh+1);
+
+ for (i = 0; i < nevents; i++) {
+ track_code = ntohl(tp->track);
+ p = hash_get(the_trackdef_hash, track_code);
+ if (p) {
+ fprintf(ofp, "track %d redefined, retain first definition\n",
+ track_code);
+ continue;
+ }
+ vec_add2(bound_tracks, btp, 1);
+ btp->track_code = track_code;
+ hash_set(the_trackdef_hash, track_code, btp - bound_tracks);
+ tp++;
+ }
+ return (0);
+}
+
+
+int event_pass (cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ event_section_header_t *eh;
+ event_entry_t *ep;
+ f64 ticks_per_us;
+ long output_count;
+ long dup_events = 0;
+ ulonglong end_time = 0;
+ double t;
+ int sp, ancestor;
+ int nevents, i;
+ u64 now;
+ u64 time0, time1;
+ double d;
+ u32 last_track_code = 0xdeafb00b;
+ u32 track_code;
+ u32 event_code, event_datum;
+ bound_track_t *tp = 0;
+ uword *p;
+
+ output_count = 0;
+ total_time = 0;
+
+ eh = (event_section_header_t *)(sh+1);
+ nevents = ntohl(eh->number_of_events);
+ ticks_per_us = ((double)ntohl(eh->clock_ticks_per_second))/1e6;
+
+ if (verbose) {
+ fprintf(ofp, "%.3f ticks_per_us\n", ticks_per_us);
+ }
+
+ ep = (event_entry_t *)(eh+1);
+
+ time0 = ntohl (ep->time[0]);
+ time1 = ntohl (ep->time[1]);
+
+ now = (((u64) time0)<<32) | time1;
+ d = now;
+ d /= ticks_per_us;
+ first_start_time = d;
+
+ for (i = 0; i < nevents; i++) {
+ time0 = ntohl (ep->time[0]);
+ time1 = ntohl (ep->time[1]);
+
+ now = (((u64) time0)<<32) | time1;
+
+ /* Convert from bus ticks to usec */
+ d = now;
+ d /= ticks_per_us;
+
+ now = d;
+
+ track_code = ntohl(ep->track);
+ event_code = ntohl(ep->event_code);
+ event_datum = ntohl(ep->event_datum);
+
+ if (track_code != last_track_code) {
+ if (tp) {
+ tp->thread_timestamp += now - tp->time_thread_on_cpu;
+ tp->time_thread_on_cpu = 0;
+ }
+ p = hash_get(the_trackdef_hash, track_code);
+ if (!p) {
+ /* synthesize a new track */
+ vec_add2(bound_tracks, tp, 1);
+ tp->track_code = track_code;
+ hash_set(the_trackdef_hash, track_code, tp - bound_tracks);
+ } else {
+ tp = bound_tracks + p[0];
+ }
+ last_track_code = track_code;
+ tp->time_thread_on_cpu = now;
+ }
+
+ if (event_code != entry_event &&
+ event_code != exit_event) {
+ ep++;
+ continue;
+ }
+
+ again:
+ switch (tp->state) {
+ case 0: /* not in state */
+ /* Another exit event? Stack pop */
+ if (event_code == exit_event) {
+ /* Only if we have something on the stack */
+ if (vec_len(tp->start_datum) > 0) {
+ tp->state = 1;
+ goto again;
+ } else {
+ fprintf (stderr,
+ "End event before start event, key 0x%x.",
+ ntohl(ep->event_datum));
+ fprintf (stderr, " Interpret results carefully...\n");
+ }
+ }
+
+ tp->state = 1;
+ if (vec_len(tp->start_datum) >= MAXSTACK) {
+ int j;
+
+ fprintf (stderr, "stack overflow..\n");
+ for (j = vec_len(tp->start_datum)-1; j >= 0; j--) {
+ fprintf(stderr, "stack[%d]: datum 0x%x\n",
+ j, tp->start_datum[j]);
+ }
+ fprintf (stderr,
+ "Stack overflow... This occurs when "
+ "(start, datum)...(end, datum) events\n"
+ "are not properly paired.\n\n"
+ "A typical scenario looks like this:\n\n"
+ " ...\n"
+ " ELOG(..., START_EVENT, datum);\n"
+ " if (condition)\n"
+ " return; /*oops, forgot the end event*/\n"
+ " ELOG(..., END_EVENT, datum);\n"
+ " ...\n\n"
+ "The datum stack dump (above) should make it clear\n"
+ "where to start looking for a sneak path...\n");
+
+ exit (1);
+ }
+ vec_add1(tp->start_datum, event_datum);
+ vec_add1(tp->start_time, (tp->thread_timestamp + (now - tp->time_thread_on_cpu)));
+#ifdef HAVING_TROUBLE
+ printf ("sp %lld key 0x%x start time %llu\n",
+ (long long) vec_len(tp->start_time)-1, event_datum,
+ (unsigned long long)
+ tp->start_time [vec_len(tp->start_time)-1]);
+ printf ("timestamp %llu, now %llu, thread on cpu %llu\n",
+ (unsigned long long) tp->thread_timestamp,
+ (unsigned long long) now,
+ (unsigned long long) tp->time_thread_on_cpu);
+#endif
+
+
+
+ /*
+ * Multiple identical enter events? If the user knows that
+ * gcc is producing bogus events due to inline functions,
+ * trash the duplicate.
+ */
+ if (inline_mokus
+ && vec_len (tp->start_datum) > 1
+ && tp->start_datum [vec_len(tp->start_datum)-1] ==
+ tp->start_datum [vec_len(tp->start_datum)-2]) {
+ vec_add1 (tp->dup_event, 1);
+ } else {
+ vec_add1 (tp->dup_event, 0);
+ }
+
+
+ ep++;
+ continue;
+
+ case 1: /* in state */
+ /* Another entry event? Stack push*/
+ if (event_code == entry_event) {
+ tp->state = 0;
+ goto again;
+ }
+
+ if (vec_len(tp->start_datum) == 0) {
+ fprintf (stderr, "Stack underflow...\n");
+ exit (1);
+ }
+
+ sp = vec_len(tp->start_time)-1;
+
+ end_time = tp->thread_timestamp + (now - tp->time_thread_on_cpu);
+
+ if (!tp->dup_event[sp]) {
+#ifdef HAVING_TROUBLE
+ printf ("sp %d key 0x%x charged %llu\n", sp,
+ tp->start_datum[sp], end_time - tp->start_time[sp]);
+ printf (" start %llu, end %llu\n", (unsigned long long) tp->start_time[sp],
+ (unsigned long long) end_time);
+#endif
+
+ record_instance (tp->start_datum[sp], (end_time -
+ tp->start_time[sp]));
+
+ /* Factor out our time from surrounding services, if any */
+ for (ancestor = sp-1; ancestor >= 0; ancestor--) {
+#ifdef HAVING_TROUBLE
+ printf ("Factor out %lld from key 0x%08x\n",
+ (end_time - tp->start_time[sp]), tp->start_datum[ancestor]);
+#endif
+ tp->start_time[ancestor] += (end_time - tp->start_time[sp]);
+ }
+ output_count++;
+ total_time += (end_time - tp->start_time[sp]);
+ tp->state = 0;
+ } else {
+ dup_events++;
+ }
+ _vec_len(tp->start_datum) = sp;
+ _vec_len(tp->start_time) = sp;
+ _vec_len(tp->dup_event) = sp;
+ }
+
+ ep++;
+ }
+ last_end_time = now;
+
+ if (scatterkey) {
+ scatterplot_data();
+ exit (0);
+ }
+
+ if (output_count) {
+ t = (double)total_time;
+ printf ("%ld instances of state, %.2f microseconds average\n",
+ output_count, t / output_count);
+
+ printf ("Total instrumented runtime: %.2f microseconds\n",
+ ((double)total_time));
+ printf ("Total runtime: %lld microseconds\n",
+ last_end_time - first_start_time);
+
+ t /= (double)(last_end_time - first_start_time);
+ t *= 100.0;
+
+ if (dup_events) {
+ printf ("Suppressed %ld duplicate state entry events\n",
+ dup_events);
+ }
+ printf ("Instrumented code accounts for %.2f%% of total time.\n\n",
+ t);
+ report_actors();
+ } else {
+ printf ("No instances of state...\n");
+ }
+
+ return(0);
+}
+
+/*
+ * Note: If necessary, add passes / columns to this table to
+ * handle section order dependencies.
+ */
+
+section_processor_t processors[CPEL_NUM_SECTION_TYPES+1] =
+{
+ {unsupported_pass}, /* type 0 -- f**ked */
+ {noop_pass}, /* type 1 -- STRTAB */
+ {noop_pass}, /* type 2 -- SYMTAB */
+ {noop_pass}, /* type 3 -- EVTDEF */
+ {trackdef_pass}, /* type 4 -- TRACKDEF */
+ {event_pass}, /* type 5 -- EVENTS */
+};
+
+int process_section(cpel_section_header_t *sh, int verbose, FILE *ofp,
+ pass_t pass)
+{
+ u32 type;
+ type = ntohl(sh->section_type);
+ int rv;
+ int (*fp)(cpel_section_header_t *, int, FILE *);
+
+ if (type > CPEL_NUM_SECTION_TYPES) {
+ fprintf(stderr, "Unknown section type %d\n", type);
+ return(1);
+ }
+ switch(pass) {
+ case PASS1:
+ fp = processors[type].pass1;
+ break;
+
+ default:
+ fprintf(stderr, "Unknown pass %d\n", pass);
+ return(1);
+ }
+
+ rv = (*fp)(sh, verbose, ofp);
+
+ return(rv);
+}
+
+char *mapfile (char *file)
+{
+ struct stat statb;
+ char *rv;
+ int maphfile;
+ size_t mapfsize;
+
+ maphfile = open (file, O_RDONLY);
+
+ if (maphfile < 0)
+ {
+ fprintf (stderr, "Couldn't read %s, skipping it...\n", file);
+ return (NULL);
+ }
+
+ if (fstat (maphfile, &statb) < 0)
+ {
+ fprintf (stderr, "Couldn't get size of %s, skipping it...\n", file);
+ return (NULL);
+ }
+
+ /* Don't try to mmap directories, FIFOs, semaphores, etc. */
+ if (! (statb.st_mode & S_IFREG)) {
+ fprintf (stderr, "%s is not a regular file, skipping it...\n", file);
+ return (NULL);
+ }
+
+ mapfsize = statb.st_size;
+
+ if (mapfsize < 3)
+ {
+ fprintf (stderr, "%s zero-length, skipping it...\n", file);
+ close (maphfile);
+ return (NULL);
+ }
+
+ rv = mmap (0, mapfsize, PROT_READ, MAP_SHARED, maphfile, 0);
+
+ if (rv == 0)
+ {
+ fprintf (stderr, "%s problem mapping, I quit...\n", file);
+ exit (-1);
+ }
+ close (maphfile);
+ return (rv);
+}
+
+int process_file (u8 *cpel, int verbose)
+{
+ cpel_file_header_t *fh;
+ cpel_section_header_t *sh;
+ u16 nsections;
+ u32 section_size;
+ int i;
+ FILE *ofp = stderr;
+
+ /* First, the file header */
+ fh = (cpel_file_header_t *)cpel;
+ if (fh->endian_version != CPEL_FILE_VERSION) {
+ if (fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) {
+ fprintf(stderr, "Little endian data format not supported\n");
+ return(1);
+ }
+ fprintf(stderr, "Unsupported file version 0x%x\n",
+ fh->endian_version);
+ return(1);
+ }
+ nsections = ntohs(fh->nsections);
+
+ /*
+ * Take a passe through the file.
+ */
+ sh = (cpel_section_header_t *)(fh+1);
+ for (i = 0; i < nsections; i++) {
+ section_size = ntohl(sh->data_length);
+
+ if(verbose) {
+ fprintf(ofp, "Section type %d, size %d\n",
+ ntohl(sh->section_type),
+ section_size);
+ }
+
+ if(process_section(sh, verbose, ofp, PASS1))
+ return(1);
+
+ sh++;
+ sh = (cpel_section_header_t *)(((u8 *)sh)+section_size);
+ }
+
+ return(0);
+}
+
+/****************************************************************************
+* main -
+****************************************************************************/
+
+int main (int argc, char **argv)
+{
+ int curarg = 1;
+ u8 *cpel = 0;
+ int verbose = 0;
+
+ if (argc < 6)
+ {
+ fprintf (stderr, "usage: cpelinreg -i <file>\n");
+ fprintf (stderr, " -s start-event --e end-event [-nokey]\n");
+ fprintf (stderr, " [-m <ninst-to-model>][-xtra-stats]\n");
+ fprintf (stderr, " [-keyscatterplot <hex-key>]\n\n");
+ fprintf (stderr, "%s\n", version);
+ exit (1);
+ }
+
+ while (curarg < argc) {
+ if (!strncmp (argv[curarg], "-ifile", 2)) {
+ curarg++;
+ g_ifile = argv[curarg++];
+ continue;
+ }
+ if (!strncmp (argv[curarg], "-start", 2)) {
+ curarg++;
+ entry_event = atol (argv [curarg++]);
+ continue;
+ }
+ if (!strncmp (argv[curarg], "-end", 2)) {
+ curarg++;
+ exit_event = atol (argv [curarg++]);
+ continue;
+ }
+
+ if (!strncmp(argv[curarg], "-badinlines", 2)) {
+ curarg++;
+ inline_mokus = 1;
+ continue;
+ }
+
+ if (!strncmp (argv[curarg], "-x", 2)) {
+ curarg++;
+ summary_stats=1;
+ continue;
+ }
+ if (!strncmp (argv[curarg], "-nokey", 2)) {
+ curarg++;
+ nokey = 1;
+ continue;
+ }
+ if (!strncmp (argv[curarg], "-keyscatterplot", 2)) {
+ curarg++;
+ sscanf (argv[curarg], "%lx", &scatterkey);
+ curarg++;
+ continue;
+ }
+
+ if (!strncmp (argv[curarg], "-model", 2)) {
+ if (model_index >= sizeof(model_these) / sizeof(int)) {
+ fprintf (stderr, "Too many model requests\n");
+ exit (1);
+ }
+ curarg++;
+ model_these[model_index++] = atol (argv [curarg++]);
+ continue;
+ }
+ if (!strncmp (argv[curarg], "-verbose", 2)) {
+ verbose++;
+ curarg++;
+ continue;
+ }
+
+ fprintf (stderr, "unknown switch '%s'\n", argv[curarg]);
+ exit (1);
+ }
+
+ cpel = (u8 *)mapfile(g_ifile);
+
+ if (cpel == NULL)
+ {
+ fprintf (stderr, "Couldn't open %s\n", g_ifile);
+ exit (1);
+ }
+
+ printf ("Extracting state info from %s\nentry_event %d, exit_event %d\n",
+ g_ifile, entry_event, exit_event);
+ if (nokey) {
+ printf ("All state instances mapped to a single actor chain\n");
+ }
+
+ the_trackdef_hash = hash_create (0, sizeof (uword));
+
+ process_file(cpel, verbose);
+ exit (0);
+}
diff --git a/perftool/cpelstate.c b/perftool/cpelstate.c
new file mode 100644
index 00000000000..3fd9ccb9c79
--- /dev/null
+++ b/perftool/cpelstate.c
@@ -0,0 +1,822 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2006-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <vppinfra/clib.h>
+#include <vppinfra/vec.h>
+#include <vppinfra/hash.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <time.h>
+#include "cpel.h"
+#include <math.h>
+
+char *time_format = "%.03d:%.02d:%.02d:%.03d:%.03d ";
+static char version[] = "cpelstate 2.0h";
+
+#define USEC_PER_MS 1000LL
+#define USEC_PER_SECOND (1000*USEC_PER_MS)
+#define USEC_PER_MINUTE (60*USEC_PER_SECOND)
+#define USEC_PER_HOUR (60*USEC_PER_MINUTE)
+
+uword *the_strtab_hash; /* (name, base-VA) hash of all string tables */
+uword *the_evtdef_hash; /* (event-id, event-definition) hash */
+uword *the_trackdef_hash; /* (track-id, track-definition) hash */
+
+f64 ticks_per_us;
+u32 state_event_code = 1; /* default: XR thread-on-cpu */
+int exclude_kernel_from_summary_stats=1;
+int summary_stats_only;
+int scatterplot;
+u8 *name_filter;
+
+typedef enum {
+ SORT_MAX_TIME=1,
+ SORT_MAX_OCCURRENCES,
+ SORT_NAME,
+} sort_t;
+
+sort_t sort_type = SORT_MAX_TIME;
+
+int widest_name_format=5;
+int widest_track_format=5;
+
+typedef struct bound_event_ {
+ u32 event_code;
+ u8 *event_str;
+ u8 *datum_str;
+ u32 is_strtab_ref;
+} bound_event_t;
+
+bound_event_t *bound_events;
+
+typedef struct bound_track_ {
+ u32 track;
+ u8 *track_str;
+ u64 *ticks_in_state; /* vector of state occurrences */
+ f64 mean_ticks_in_state;
+ f64 variance_ticks_in_state;
+ f64 total_ticks_in_state;
+} bound_track_t;
+
+bound_track_t *bound_tracks;
+
+void fatal(char *s)
+{
+ fprintf(stderr, "%s", s);
+ exit(1);
+}
+
+typedef enum {
+ PASS1=1,
+ PASS2=2,
+} pass_t;
+
+typedef struct {
+ int (*pass1)(cpel_section_header_t *, int, FILE *);
+ int (*pass2)(cpel_section_header_t *, int, FILE *);
+} section_processor_t;
+
+int bad_section(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ fprintf(ofp, "Bad (type 0) section, skipped...\n");
+ return(0);
+}
+
+int noop_pass(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ return(0);
+}
+
+int strtab_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ uword *p;
+ u8 *strtab_data_area = (u8 *)(sh+1);
+
+ /* Multiple string tables with the same name are Bad... */
+ p = hash_get_mem(the_strtab_hash, strtab_data_area);
+ if (p) {
+ fprintf(ofp, "Duplicate string table name %s", strtab_data_area);
+ }
+ /*
+ * Looks funny, but we really do want key = first string in the
+ * table, value = address(first string in the table)
+ */
+ hash_set_mem(the_strtab_hash, strtab_data_area, strtab_data_area);
+ if (verbose) {
+ fprintf(ofp, "String Table %s\n", strtab_data_area);
+ }
+ return(0);
+}
+
+int evtdef_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ int i, nevents;
+ event_definition_section_header_t *edh;
+ event_definition_t *ep;
+ u8 *this_strtab;
+ u32 event_code;
+ uword *p;
+ bound_event_t *bp;
+ int thislen;
+
+ edh = (event_definition_section_header_t *)(sh+1);
+ nevents = ntohl(edh->number_of_event_definitions);
+
+ if (verbose) {
+ fprintf(ofp, "Event Definition Section: %d definitions\n",
+ nevents);
+ }
+
+ p = hash_get_mem(the_strtab_hash, edh->string_table_name);
+ if (!p) {
+ fprintf(ofp, "Fatal: couldn't find string table\n");
+ return(1);
+ }
+ this_strtab = (u8 *)p[0];
+
+ ep = (event_definition_t *)(edh+1);
+
+ for (i = 0; i < nevents; i++) {
+ event_code = ntohl(ep->event);
+ p = hash_get(the_evtdef_hash, event_code);
+ if (p) {
+ fprintf(ofp, "Event %d redefined, retain first definition\n",
+ event_code);
+ continue;
+ }
+ vec_add2(bound_events, bp, 1);
+ bp->event_code = event_code;
+ bp->event_str = this_strtab + ntohl(ep->event_format);
+ bp->datum_str = this_strtab + ntohl(ep->datum_format);
+ bp->is_strtab_ref = 0;
+ /* Decide if the datum format is a %s format => strtab reference */
+ {
+ int j;
+ int seen_percent=0;
+
+ for (j = 0; j < strlen((char *)(bp->datum_str)); j++) {
+ if (bp->datum_str[j] == '%'){
+ seen_percent=1;
+ continue;
+ }
+ if (seen_percent && bp->datum_str[j] == 's') {
+ bp->is_strtab_ref = 1;
+ }
+ }
+ }
+
+ hash_set(the_evtdef_hash, event_code, bp - bound_events);
+
+ thislen = strlen((char *)bp->event_str);
+ if (thislen > widest_name_format)
+ widest_name_format = thislen;
+
+ ep++;
+ }
+ return (0);
+}
+
+int trackdef_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ int i, nevents;
+ track_definition_section_header_t *tdh;
+ track_definition_t *tp;
+ u8 *this_strtab;
+ u32 track_code;
+ uword *p;
+ bound_track_t *btp;
+ int thislen;
+
+ tdh = (track_definition_section_header_t *)(sh+1);
+ nevents = ntohl(tdh->number_of_track_definitions);
+
+ if (verbose) {
+ fprintf(ofp, "Track Definition Section: %d definitions\n",
+ nevents);
+ }
+
+ p = hash_get_mem(the_strtab_hash, tdh->string_table_name);
+ if (!p) {
+ fprintf(ofp, "Fatal: couldn't find string table\n");
+ return(1);
+ }
+ this_strtab = (u8 *)p[0];
+
+ tp = (track_definition_t *)(tdh+1);
+
+ for (i = 0; i < nevents; i++) {
+ track_code = ntohl(tp->track);
+ p = hash_get(the_trackdef_hash, track_code);
+ if (p) {
+ fprintf(ofp, "track %d redefined, retain first definition\n",
+ track_code);
+ continue;
+ }
+ vec_add2(bound_tracks, btp, 1);
+ btp->track = track_code;
+ btp->track_str = this_strtab + ntohl(tp->track_format);
+ hash_set(the_trackdef_hash, track_code, btp - bound_tracks);
+
+ thislen = strlen((char *)(btp->track_str));
+ if (thislen > widest_track_format)
+ widest_track_format = thislen;
+ tp++;
+ }
+ return (0);
+}
+
+int unsupported_pass (cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ if (verbose) {
+ fprintf(ofp, "Unsupported type %d section\n",
+ ntohl(sh->section_type));
+ }
+ return(0);
+}
+
+int event_pass2(cpel_section_header_t *sh, int verbose, FILE *ofp)
+{
+ event_section_header_t *eh;
+ u32 track_code;
+ int nevents;
+ int i;
+ uword *p;
+ event_entry_t *ep;
+ u64 now;
+ u32 time0, time1;
+ bound_track_t generic_track;
+ u32 last_track_code;
+ u64 state_start_ticks=0;
+ u64 ticks_in_state;
+ bound_track_t *state_track=0;
+ int in_state=0;
+ generic_track.track_str = (u8 *) "%d";
+ last_track_code = 0xdeafbeef;
+
+ eh = (event_section_header_t *)(sh+1);
+ nevents = ntohl(eh->number_of_events);
+ ticks_per_us = ((double)ntohl(eh->clock_ticks_per_second))/1e6;
+
+ if (verbose) {
+ fprintf(ofp, "%.3f ticks_per_us\n", ticks_per_us);
+ }
+
+ ep = (event_entry_t *)(eh+1);
+
+ p = hash_get_mem(the_strtab_hash, eh->string_table_name);
+ if (!p) {
+ fprintf(ofp, "Fatal: couldn't find string table\n");
+ return(1);
+ }
+
+ for (i = 0; i < nevents; i++) {
+ time0 = ntohl (ep->time[0]);
+ time1 = ntohl (ep->time[1]);
+
+ now = (((u64) time0)<<32) | time1;
+
+ /* Found the state-change event ? */
+ if (ntohl(ep->event_code) == state_event_code) {
+ /*
+ * Add a ticks-in-state record, unless
+ * this is the "prime mover" event instance
+ */
+ if (in_state) {
+ ticks_in_state = now - state_start_ticks;
+ vec_add1(state_track->ticks_in_state, ticks_in_state);
+ }
+ /* switch to now-current track */
+ state_start_ticks = now;
+ track_code = ntohl(ep->track);
+ if (track_code != last_track_code) {
+ p = hash_get(the_trackdef_hash, track_code);
+ if (p) {
+ state_track = &bound_tracks[p[0]];
+ } else {
+ state_track = &generic_track;
+ }
+ last_track_code = track_code;
+ }
+ in_state = 1;
+ }
+ ep++;
+ }
+ return(0);
+}
+
+/*
+ * Note: If necessary, add passes / columns to this table to
+ * handle section order dependencies.
+ */
+
+section_processor_t processors[CPEL_NUM_SECTION_TYPES+1] =
+{
+ {bad_section, noop_pass}, /* type 0 -- f**ked */
+ {strtab_pass1, noop_pass}, /* type 1 -- STRTAB */
+ {unsupported_pass, noop_pass}, /* type 2 -- SYMTAB */
+ {evtdef_pass1, noop_pass}, /* type 3 -- EVTDEF */
+ {trackdef_pass1, noop_pass}, /* type 4 -- TRACKDEF */
+ {noop_pass, event_pass2}, /* type 5 -- EVENTS */
+};
+
+
+int process_section(cpel_section_header_t *sh, int verbose, FILE *ofp,
+ pass_t pass)
+{
+ u32 type;
+ type = ntohl(sh->section_type);
+ int rv;
+ int (*fp)(cpel_section_header_t *, int, FILE *);
+
+ if (type > CPEL_NUM_SECTION_TYPES) {
+ fprintf(stderr, "Unknown section type %d\n", type);
+ return(1);
+ }
+ switch(pass) {
+ case PASS1:
+ fp = processors[type].pass1;
+ break;
+
+ case PASS2:
+ fp = processors[type].pass2;
+ break;
+
+ default:
+ fprintf(stderr, "Unknown pass %d\n", pass);
+ return(1);
+ }
+
+ rv = (*fp)(sh, verbose, ofp);
+
+ return(rv);
+}
+
+int cpel_dump_file_header(cpel_file_header_t *fh, int verbose, FILE *ofp)
+{
+ time_t file_time;
+
+ if (verbose) {
+ fprintf(ofp, "CPEL file: %s-endian, version %d\n",
+ ((fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) ?
+ "little" : "big"),
+ fh->endian_version & CPEL_FILE_VERSION_MASK);
+
+ file_time = ntohl(fh->file_date);
+
+ fprintf(ofp, "File created %s", ctime(&file_time));
+ fprintf(ofp, "File has %d sections\n",
+ ntohs(fh->nsections));
+ }
+
+ return(0);
+}
+
+
+int cpel_dump(u8 *cpel, int verbose, FILE *ofp)
+{
+ cpel_file_header_t *fh;
+ cpel_section_header_t *sh;
+ u16 nsections;
+ u32 section_size;
+ int i;
+
+ /* First, the file header */
+ fh = (cpel_file_header_t *)cpel;
+ if (fh->endian_version != CPEL_FILE_VERSION) {
+ if (fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) {
+ fprintf(stderr, "Little endian data format not supported\n");
+ return(1);
+ }
+ fprintf(stderr, "Unsupported file version 0x%x\n",
+ fh->endian_version);
+ return(1);
+ }
+ cpel_dump_file_header(fh, verbose, ofp);
+ nsections = ntohs(fh->nsections);
+
+ /*
+ * Take two passes through the file. PASS1 builds
+ * data structures, PASS2 actually dumps the file.
+ * Just in case the sections are in an unobvious order.
+ */
+ sh = (cpel_section_header_t *)(fh+1);
+ for (i = 0; i < nsections; i++) {
+ section_size = ntohl(sh->data_length);
+
+ if(verbose) {
+ fprintf(ofp, "Section type %d, size %d\n", ntohl(sh->section_type),
+ section_size);
+ }
+
+ if(process_section(sh, verbose, ofp, PASS1))
+ return(1);
+
+ sh++;
+ sh = (cpel_section_header_t *)(((u8 *)sh)+section_size);
+ }
+
+ sh = (cpel_section_header_t *)(fh+1);
+ for (i = 0; i < nsections; i++) {
+ if(process_section(sh, verbose, ofp, PASS2))
+ return(1);
+ section_size = ntohl(sh->data_length);
+ sh++;
+ sh = (cpel_section_header_t *)(((u8 *)sh)+section_size);
+ }
+ return(0);
+}
+
+void compute_state_statistics(int verbose, FILE *ofp)
+{
+ int i, j;
+ bound_track_t *bp;
+ f64 fticks;
+
+ /* Across the bound tracks */
+ for (i = 0; i < vec_len(bound_tracks); i++) {
+ bp = &bound_tracks[i];
+ bp->mean_ticks_in_state = 0.0;
+ bp->variance_ticks_in_state = 0.0;
+ bp->total_ticks_in_state = 0.0;
+ for (j = 0; j < vec_len(bp->ticks_in_state); j++) {
+ bp->total_ticks_in_state += (f64) bp->ticks_in_state[j];
+ }
+ /* Compute mean */
+ if (vec_len(bp->ticks_in_state)) {
+ bp->mean_ticks_in_state = bp->total_ticks_in_state /
+ ((f64) vec_len(bp->ticks_in_state));
+ }
+ /* Accumulate sum: (Xi-Xbar)**2 */
+ for (j = 0; j < vec_len(bp->ticks_in_state); j++) {
+ fticks = bp->ticks_in_state[j];
+ bp->variance_ticks_in_state +=
+ (fticks - bp->mean_ticks_in_state)*
+ (fticks - bp->mean_ticks_in_state);
+ }
+ /* Compute s**2, the unbiased estimator of sigma**2 */
+ if (vec_len(bp->ticks_in_state) > 1) {
+ bp->variance_ticks_in_state /= (f64)
+ (vec_len(bp->ticks_in_state)-1);
+ }
+ }
+}
+
+int track_compare_max (const void *arg1, const void *arg2)
+{
+ bound_track_t *a1 = (bound_track_t *)arg1;
+ bound_track_t *a2 = (bound_track_t *)arg2;
+ f64 v1, v2;
+
+ v1 = a1->total_ticks_in_state;
+ v2 = a2->total_ticks_in_state;
+
+ if (v1 < v2)
+ return (1);
+ else if (v1 == v2)
+ return (0);
+ else return (-1);
+}
+
+int track_compare_occurrences (const void *arg1, const void *arg2)
+{
+ bound_track_t *a1 = (bound_track_t *)arg1;
+ bound_track_t *a2 = (bound_track_t *)arg2;
+ f64 v1, v2;
+
+ v1 = (f64) vec_len(a1->ticks_in_state);
+ v2 = (f64) vec_len(a2->ticks_in_state);
+
+ if (v1 < v2)
+ return (1);
+ else if (v1 == v2)
+ return (0);
+ else return (-1);
+}
+
+int track_compare_name (const void *arg1, const void *arg2)
+{
+ bound_track_t *a1 = (bound_track_t *)arg1;
+ bound_track_t *a2 = (bound_track_t *)arg2;
+
+ return (strcmp((char *)(a1->track_str), (char *)(a2->track_str)));
+}
+
+void sort_state_statistics(sort_t type, FILE *ofp)
+{
+ int (*compare)(const void *, const void *)=0;
+
+ if (summary_stats_only)
+ return;
+
+ switch(type) {
+ case SORT_MAX_TIME:
+ fprintf(ofp, "Results sorted by max time in state.\n");
+ compare = track_compare_max;
+ break;
+
+ case SORT_MAX_OCCURRENCES:
+ fprintf(ofp, "Results sorted by max occurrences of state.\n");
+ compare = track_compare_occurrences;
+ break;
+
+ case SORT_NAME:
+ compare = track_compare_name;
+ fprintf(ofp, "Results sorted by process-id/name/thread ID\n");
+ break;
+
+ default:
+ fatal("sort type not set?");
+ }
+
+ qsort (bound_tracks, vec_len(bound_tracks),
+ sizeof (bound_track_t), compare);
+}
+
+void print_state_statistics(int verbose, FILE *ofp)
+{
+ int i,j;
+ u8 *trackpad;
+ bound_track_t *bp;
+ f64 total_time = 0.0;
+ f64 total_switches = 0.0;
+
+ trackpad = format(0, "%%-%ds ", widest_track_format);
+ vec_add1(trackpad, 0);
+
+ if (!summary_stats_only) {
+ fprintf(ofp, (char *)trackpad, "ProcThread");
+ fprintf(ofp, " Mean(us) Stdev(us) Total(us) N\n");
+ }
+
+ for (i = 0; i < vec_len(bound_tracks); i++) {
+ bp = &bound_tracks[i];
+ if (bp->mean_ticks_in_state == 0.0)
+ continue;
+
+ if (name_filter &&
+ strncmp((char *)(bp->track_str), (char *)name_filter,
+ strlen((char *)name_filter)))
+ continue;
+
+ /*
+ * Exclude kernel threads (e.g. idle thread) from
+ * state statistics
+ */
+ if (exclude_kernel_from_summary_stats &&
+ !strncmp((char *)(bp->track_str), "kernel ", 7))
+ continue;
+
+ total_switches += (f64) vec_len(bp->ticks_in_state);
+
+ if (!summary_stats_only) {
+ fprintf(ofp, (char *) trackpad, bp->track_str);
+ fprintf(ofp, "%10.3f +- %10.3f",
+ bp->mean_ticks_in_state / ticks_per_us,
+ sqrt(bp->variance_ticks_in_state)
+ / (f64) ticks_per_us);
+ fprintf(ofp, "%12.3f",
+ bp->total_ticks_in_state / ticks_per_us);
+ fprintf(ofp, "%8d\n", (int)vec_len(bp->ticks_in_state));
+ }
+
+ if (scatterplot) {
+ for (j = 0; j < vec_len(bp->ticks_in_state); j++) {
+ fprintf(ofp, "%.3f\n",
+ (f64)bp->ticks_in_state[j] / ticks_per_us);
+ }
+ }
+
+ total_time += bp->total_ticks_in_state;
+ }
+
+ if (!summary_stats_only)
+ fprintf(ofp, "\n");
+ fprintf(ofp, "Note: the following statistics %s kernel-thread activity.\n",
+ exclude_kernel_from_summary_stats ? "exclude" : "include");
+ if (name_filter)
+ fprintf(ofp,
+ "Note: only pid/proc/threads matching '%s' are included.\n",
+ name_filter);
+
+ fprintf(ofp,
+ "Total runtime: %10.3f (us), Total state switches: %.0f\n",
+ total_time / ticks_per_us, total_switches);
+ fprintf(ofp, "Average time in state: %10.3f (us)\n",
+ (total_time / total_switches) / ticks_per_us);
+}
+
+char *mapfile (char *file)
+{
+ struct stat statb;
+ char *rv;
+ int maphfile;
+ size_t mapfsize;
+
+ maphfile = open (file, O_RDONLY);
+
+ if (maphfile < 0)
+ {
+ fprintf (stderr, "Couldn't read %s, skipping it...\n", file);
+ return (NULL);
+ }
+
+ if (fstat (maphfile, &statb) < 0)
+ {
+ fprintf (stderr, "Couldn't get size of %s, skipping it...\n", file);
+ return (NULL);
+ }
+
+ /* Don't try to mmap directories, FIFOs, semaphores, etc. */
+ if (! (statb.st_mode & S_IFREG)) {
+ fprintf (stderr, "%s is not a regular file, skipping it...\n", file);
+ return (NULL);
+ }
+
+ mapfsize = statb.st_size;
+
+ if (mapfsize < 3)
+ {
+ fprintf (stderr, "%s zero-length, skipping it...\n", file);
+ close (maphfile);
+ return (NULL);
+ }
+
+ rv = mmap (0, mapfsize, PROT_READ, MAP_SHARED, maphfile, 0);
+
+ if (rv == 0)
+ {
+ fprintf (stderr, "%s problem mapping, I quit...\n", file);
+ exit (-1);
+ }
+ close (maphfile);
+ return (rv);
+}
+
+/*
+ * main
+ */
+int main (int argc, char **argv)
+{
+ char *cpel_file = 0;
+ char *outputfile = 0;
+ FILE *ofp;
+ char *cpel;
+ int verbose=0;
+ int curarg=1;
+
+ while (curarg < argc) {
+ if (!strncmp(argv[curarg], "--input-file", 3)) {
+ curarg++;
+ if (curarg < argc) {
+ cpel_file = argv[curarg];
+ curarg++;
+ continue;
+ }
+ fatal("Missing filename after --input-file\n");
+ }
+ if (!strncmp(argv[curarg], "--output-file", 3)) {
+ curarg ++;
+ if (curarg < argc) {
+ outputfile = argv[curarg];
+ curarg ++;
+ continue;
+ }
+ fatal("Missing filename after --output-file\n");
+ }
+ if (!strncmp(argv[curarg], "--verbose", 3)) {
+ curarg++;
+ verbose++;
+ continue;
+ }
+ if (!strncmp(argv[curarg], "--scatterplot", 4)) {
+ curarg++;
+ scatterplot=1;
+ continue;
+ }
+
+ if (!strncmp(argv[curarg], "--state-event", 4)) {
+ curarg++;
+ if (curarg < argc) {
+ state_event_code = atol(argv[curarg]);
+ curarg ++;
+ continue;
+ }
+ fatal("Missing integer after --state-event\n");
+ }
+ if (!strncmp(argv[curarg], "--max-time-sort", 7)) {
+ sort_type = SORT_MAX_TIME;
+ curarg++;
+ continue;
+ }
+ if (!strncmp(argv[curarg], "--max-occurrence-sort", 7)) {
+ sort_type = SORT_MAX_OCCURRENCES;
+ curarg++;
+ continue;
+ }
+ if (!strncmp(argv[curarg], "--name-sort", 3)) {
+ sort_type = SORT_NAME;
+ curarg++;
+ continue;
+ }
+ if (!strncmp(argv[curarg], "--kernel-included", 3)) {
+ exclude_kernel_from_summary_stats = 0;
+ curarg++;
+ continue;
+ }
+ if (!strncmp(argv[curarg], "--summary", 3)) {
+ summary_stats_only=1;
+ curarg++;
+ continue;
+ }
+ if (!strncmp(argv[curarg], "--filter", 3)) {
+ curarg ++;
+ if (curarg < argc) {
+ name_filter = (u8 *)argv[curarg];
+ curarg ++;
+ continue;
+ }
+ fatal("Missing filter string after --filter\n");
+ }
+
+
+ usage:
+ fprintf(stderr,
+ "cpelstate --input-file <filename> [--output-file <filename>]\n");
+ fprintf(stderr,
+ " [--state-event <decimal>] [--verbose]\n");
+ fprintf(stderr,
+ " [--max-time-sort(default) | --max-occurrence-sort |\n");
+
+ fprintf(stderr,
+ " --name-sort-sort] [--kernel-included]\n");
+
+ fprintf(stderr,
+ " [--summary-stats-only] [--scatterplot]\n");
+
+ fprintf(stderr, "%s\n", version);
+ exit(1);
+ }
+
+ if (cpel_file == 0)
+ goto usage;
+
+ cpel = mapfile(cpel_file);
+ if (cpel == 0) {
+ fprintf(stderr, "Couldn't map %s...\n", cpel_file);
+ exit(1);
+ }
+
+ if (!outputfile) {
+ ofp = fdopen(1, "w");
+ if (ofp == NULL) {
+ fprintf(stderr, "Couldn't fdopen(1)?\n");
+ exit(1);
+ }
+ } else {
+ ofp = fopen(outputfile, "w");
+ if (ofp == NULL) {
+ fprintf(stderr, "Couldn't create %s...\n", outputfile);
+ exit(1);
+ }
+ }
+
+ the_strtab_hash = hash_create_string (0, sizeof (uword));
+ the_evtdef_hash = hash_create (0, sizeof (uword));
+ the_trackdef_hash = hash_create (0, sizeof (uword));
+
+ if (cpel_dump((u8 *) cpel, verbose, ofp)) {
+ if (outputfile)
+ unlink(outputfile);
+ }
+
+ compute_state_statistics(verbose, ofp);
+ sort_state_statistics(sort_type, ofp);
+ print_state_statistics(verbose, ofp);
+
+ fclose(ofp);
+ return(0);
+}
diff --git a/perftool/delsvec.c b/perftool/delsvec.c
new file mode 100644
index 00000000000..724935d331e
--- /dev/null
+++ b/perftool/delsvec.c
@@ -0,0 +1,315 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2006-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Break up a delimited string into a vector of substrings */
+
+#include <stdio.h>
+#include <vppinfra/clib.h>
+#include <vppinfra/vec.h>
+#include <vppinfra/hash.h>
+#include <stdarg.h>
+
+/*
+ * #define UNIT_TESTS 1
+ * #define MATCH_TRACE 1
+ */
+
+/*
+ * delsvec
+ * break up an input string into a vector of [null-terminated] u8 *'s
+ *
+ * Each supplied delimiter character results in a string in the output
+ * vector, unless the delimiters occur back-to-back. When matched,
+ * a whitespace character in the delimiter consumes an arbitrary
+ * run of whitespace. See the unit tests at the end of this file
+ * for a set of examples.
+ *
+ * Returns a u8 **, or NULL if the input fails to match. It is assumed
+ * that both input and fmt are C strings, not necessarily vectors.
+ *
+ * Output strings are both vectors and proper C strings.
+ */
+
+static u8 **string_cache;
+static u8 **svec_cache;
+
+void delsvec_recycle_this_string (u8 *s)
+{
+ if (s) {
+ _vec_len (s) = 0;
+ vec_add1(string_cache, s);
+ }
+}
+
+void delsvec_recycle_this_svec (u8 **svec)
+{
+ if (svec) {
+ if (svec_cache) {
+ vec_free (svec_cache);
+ }
+ _vec_len (svec) = 0;
+ svec_cache = svec;
+ }
+}
+
+int pvl (char *a)
+{
+ return vec_len(a);
+}
+
+u8 **delsvec(void *input_arg, char *fmt)
+{
+ u8 **rv = 0;
+ int input_index=0;
+ u8 *this;
+ int dirflag=0;
+ int i;
+ u8 *input = input_arg;
+
+ if (svec_cache) {
+ rv = svec_cache;
+ svec_cache = 0;
+ }
+
+ while (fmt) {
+ dirflag=0;
+ if (vec_len (string_cache) > 0) {
+ this = string_cache [vec_len(string_cache)-1];
+ _vec_len (string_cache) = vec_len (string_cache) - 1;
+ } else
+ this = 0;
+ /*
+ * '*' means one of two things: match the rest of the input,
+ * or match as many characters as possible
+ */
+ if (fmt[0] == '*') {
+ fmt++;
+ dirflag=1;
+ /*
+ * no more format: eat rest of string...
+ */
+ if (!fmt[0]) {
+ for (;input[input_index]; input_index++)
+ vec_add1(this, input[input_index]);
+ if (vec_len(this)) {
+ vec_add1(this, 0);
+#ifdef MATCH_TRACE
+ printf("final star-match adds: '%s'\n", this);
+#endif
+ vec_add1(rv, this);
+ } else {
+ vec_add1(string_cache, this);
+ }
+
+ return(rv);
+ }
+ }
+ /*
+ * Left-to-right scan, adding chars until next delimiter char
+ * appears.
+ */
+ if (!dirflag) {
+ while (input[input_index]) {
+ if (input[input_index] == fmt[0]) {
+ /* If we just (exact) matched a whitespace delimiter */
+ if (fmt[0] == ' '){
+ /* scan forward eating whitespace */
+ while (input[input_index] == ' ' ||
+ input[input_index] == '\t' ||
+ input[input_index] == '\n')
+ input_index++;
+ input_index--;
+ }
+ goto found;
+ }
+ /* If we're looking for whitespace */
+ if (fmt[0] == ' ') {
+ /* and we have whitespace */
+ if (input[input_index] == ' ' ||
+ input[input_index] == '\t' ||
+ input[input_index] == '\n') {
+ /* scan forward eating whitespace */
+ while (input[input_index] == ' ' ||
+ input[input_index] == '\t' ||
+ input[input_index] == '\n') {
+ input_index++;
+ }
+ input_index--;
+ goto found;
+ }
+ }
+ /* Not a delimiter, save it */
+ vec_add1(this, input[input_index]);
+ input_index++;
+ }
+ /*
+ * Fell off the wagon, clean up and bail out
+ */
+ bail:
+
+#ifdef MATCH_TRACE
+ printf("failed, fmt[0] = '%c', input[%d]='%s'\n",
+ fmt[0], input_index, &input[input_index]);
+#endif
+ delsvec_recycle_this_string(this);
+ for (i = 0; i < vec_len(rv); i++)
+ delsvec_recycle_this_string(rv[i]);
+ delsvec_recycle_this_svec(rv);
+ return(0);
+
+ found:
+ /*
+ * Delimiter matched
+ */
+ input_index++;
+ fmt++;
+ /*
+ * If we actually accumulated non-delimiter characters,
+ * add them to the result vector
+ */
+ if (vec_len(this)) {
+ vec_add1(this, 0);
+#ifdef MATCH_TRACE
+ printf("match: add '%s'\n", this);
+#endif
+ vec_add1(rv, this);
+ } else {
+ vec_add1(string_cache, this);
+ }
+ } else {
+ /*
+ * right-to-left scan, '*' not at
+ * the end of the delimiter string
+ */
+ i = input_index;
+ while (input[++i])
+ ; /* scan forward */
+ i--;
+ while (i > input_index) {
+ if (input[i] == fmt[0])
+ goto found2;
+
+ if (fmt[0] == ' ' || fmt[0] == '\t' ||
+ fmt[0] == '\n') {
+ if (input[i] == ' ' ||
+ input[i] == '\t' ||
+ input[i] == '\n')
+ goto found2;
+ }
+ i--;
+ }
+ goto bail;
+
+ found2:
+ for (; input_index < i; input_index++) {
+ vec_add1(this, input[input_index]);
+ }
+ input_index++;
+ fmt++;
+ vec_add1(this, 0);
+#ifdef MATCH_TRACE
+ printf("inner '*' match: add '%s'\n", this);
+#endif
+ vec_add1(rv, this);
+ }
+ }
+ return (rv);
+}
+
+#ifdef UNIT_TESTS
+
+typedef struct utest_ {
+ char *string;
+ char *fmt;
+} utest_t;
+
+utest_t tests[] = {
+#ifdef NOTDEF
+ {"Dec 7 08:56",
+ " :*"},
+ {"Dec 17 08:56",
+ " :*"},
+ {"Dec 7 08:56:41.239 install/inst_repl 0/9/CPU0 t1 [40989] File List:Successfully blobbified file list. Took 1 milliseconds",
+ " ::. / // [] *"},
+ {"RP/0/9/CPU0:Dec 7 08:55:28.550 : sam_server[291]: SAM backs up digest list to memory file",
+ "///: ::. : []: *"},
+ /* Expected to fail */
+ {"Dec 7 08:56:41.239 install/inst_repl 0/9/CPU0 t1 [40989] File List:Successfully blobbified file list. Took 1 milliseconds",
+ "///: ::. : : *"},
+ /* Expected to fail */
+ {"RP/0/9/CPU0:Dec 7 08:55:28.550 : sam_server[291]: SAM backs up digest list to memory file",
+ " ::. / // [] *"},
+ {"THIS that and + theother", "*+ *"},
+ {"Dec 12 15:33:07.103 ifmgr/errors 0/RP0/CPU0 3# t2 Failed to open IM connection: No such file or directory", " ::. / // *"},
+ {"Dec 16 21:43:47.328 ifmgr/bulk 0/3/CPU0 t8 Bulk DPC async download complete. Partitions 1, node_count 1, total_out 0, out_offset 0, out_expected 0: No error"," ::. / // *"},
+ {"t:0x53034bd6 CPU:00 PROCESS :PROCCREATE_NAME",
+ ": : :*"},
+ {" pid:1", " *"},
+ {"t:0x53034cbb CPU:00 THREAD :THCREATE pid:1 tid:1",
+ ": : : pid: tid:*"},
+ {"t:0x5303f950 CPU:00 COMM :REC_PULSE scoid:0x40000003 pid:364659",
+ ": : : *"},
+ {"/hfr-base-3.3.85/lib/libttyconnection.dll 0xfc000000 0x0000306c 0xfc027000 0x000001c8 1",
+ " *"},
+ {"Feb 28 02:38:26.123 seqtrace 0/1/CPU0 t8 :msg_receive:ifmgr/t8:IMC_MSG_MTU_UPDATE:ppp_ma/t1",
+ " ::. // ::::*"},
+
+ {"Feb 28 02:38:26.123 seqtrace 0/1/CPU0 t8 :msg_send_event:call:ifmgr/t8:124/0:cdp/t1",
+ " ::. // :msg_send_event::::*"},
+
+ {"Feb 28 02:38:26.125 seqtrace 0/1/CPU0 t1 :msg_receive_event:cdp/t1:124/0",
+ " ::. // :msg_receive_event::*"}
+ {"t:0x645dd86d CPU:00 USREVENT:EVENT:100, d0:0x00000002 d1:0x00000000",
+ ": : USREVENT:EVENT:, d0: *"}
+ {"t:0x5303f950 CPU:00 COMM :REC_PULSE scoid:0x40000003 pid:364659",
+ ": : : *"},
+ {"t:0x2ccf9f5a CPU:00 INT_ENTR:0x80000000 (-2147483648) IP:0x002d8b18",
+ ": : INT_ENTR: IP:*"}
+ {"t:0xd473951c CPU:00 KER_EXIT:SCHED_GET/88 ret_val:2 sched_priority:10",
+ ": : KER_EXIT:SCHED_GET : sched_priority:*"}
+ {"t:0x00000123 CPU:01 SYSTEM :FUNC_ENTER thisfn:0x40e62048 call_site:0x00000000",
+ ": : SYSTEM :FUNC_ thisfn: *"},
+ {"t:0x5af8de95 CPU:00 INT_HANDLER_ENTR:0x0000004d (77) PID:8200 IP:0x00000000 AREA:0x0bf9b290", ": : INT_HANDLER_*"},
+#endif
+ {"t:0x6d1ff92f CPU:00 CONTROL: BUFFER sequence = 1053, num_events = 714",
+ ": : CONTROL*"},
+ {"t:0x6d1ff92f CPU:00 CONTROL :TIME msb:0x0000003c lsb(offset):0x6d1ff921",
+ ": : CONTROL*"},
+};
+
+int main (int argc, char **argv)
+{
+ int i, j;
+ u8 **svec;
+
+ for (j = 0; j < ARRAY_LEN(tests); j++) {
+ printf ("input string: '%s'\n", tests[j].string);
+ printf ("delimiter arg: '%s'\n", tests[j].fmt);
+ printf ("parse trace:\n");
+ svec = delsvec(tests[j].string, tests[j].fmt);
+ if (!svec) {
+ printf("index %d failed\n", j);
+ continue;
+ }
+ printf("%d substring vectors\n", vec_len(svec));
+ for (i = 0; i < vec_len(svec); i++) {
+ printf("[%d]: '%s'\n", i, svec[i]);
+ }
+ printf ("-------------------\n");
+ }
+ exit(0);
+}
+#endif
diff --git a/perftool/linreg.c b/perftool/linreg.c
new file mode 100644
index 00000000000..084091bb907
--- /dev/null
+++ b/perftool/linreg.c
@@ -0,0 +1,78 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2006-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* see "Numerical Recipies in C, 2nd ed." p 665 */
+
+#include <stdio.h>
+#include <math.h>
+
+/*
+ * linreg
+ * Linear regression of (xi, yi), returns parameters for least-squares
+ * fit y = a + bx. Also, compute Pearson's R.
+ */
+void linreg (double *x, double *y, int nitems, double *a, double *b,
+ double *minp, double *maxp, double *meanp, double *r)
+{
+ double sx = 0.0;
+ double sy = 0.0;
+ double st2 = 0.0;
+ double min = y[0];
+ double max = 0.0;
+ double ss, meanx, meany, t;
+ double errx, erry, prodsum, sqerrx, sqerry;
+ int i;
+
+ *b = 0.0;
+
+ for (i = 0; i < nitems; i++) {
+ sx += x[i];
+ sy += y[i];
+ if (y[i] < min)
+ min = y[i];
+ if (y[i] > max)
+ max = y[i];
+ }
+ ss = nitems;
+ meanx = sx / ss;
+ meany = *meanp = sy / ss;
+ *minp = min;
+ *maxp = max;
+
+ for (i = 0; i < nitems; i++) {
+ t = x[i] - meanx;
+ st2 += t*t;
+ *b += t*y[i];
+ }
+
+ *b /= st2;
+ *a = (sy-sx*(*b))/ss;
+
+ prodsum = 0.0;
+ sqerrx = 0.0;
+ sqerry = 0.0;
+
+ /* Compute numerator of Pearson's R */
+ for (i = 0; i < nitems; i++) {
+ errx = x[i] - meanx;
+ erry = y[i] - meany;
+ prodsum += errx * erry;
+ sqerrx += errx*errx;
+ sqerry += erry*erry;
+ }
+
+ *r = prodsum / (sqrt(sqerrx)*sqrt(sqerry));
+}
diff --git a/perftool/new.cpel b/perftool/new.cpel
new file mode 100644
index 00000000000..b0f35958dc0
--- /dev/null
+++ b/perftool/new.cpel
Binary files differ
diff --git a/perftool/new.elog b/perftool/new.elog
new file mode 100644
index 00000000000..2d99bb16b82
--- /dev/null
+++ b/perftool/new.elog
Binary files differ
diff --git a/perftool/props.c b/perftool/props.c
new file mode 100644
index 00000000000..84af5b1c648
--- /dev/null
+++ b/perftool/props.c
@@ -0,0 +1,280 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2006-2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <malloc.h>
+#include <time.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+static char *sxerox (char *s);
+
+#define NBUCKETS 97
+
+typedef struct prop_ {
+ struct prop_ *next;
+ char *name;
+ char *value;
+} prop_t;
+
+static prop_t *buckets [NBUCKETS];
+static int hash_shifts[4] = {24, 16, 8, 0};
+
+/*
+ * getprop
+ */
+
+char *getprop (char *name)
+{
+ unsigned char *cp;
+ unsigned long hash=0;
+ prop_t *bp;
+ int i=0;
+
+ for (cp = (unsigned char *) name; *cp; cp++)
+ hash ^= (*cp)<<(hash_shifts[(i++)&0x3]);
+
+ bp = buckets [hash%NBUCKETS];
+
+ while (bp && strcmp(bp->name, name)) {
+ bp = bp->next;
+ }
+
+ if (bp == NULL)
+ return (0);
+ else
+ return (bp->value);
+}
+
+/*
+ * getprop_default
+ */
+
+char *getprop_default (char *name, char *def)
+{
+ char *rv;
+ rv = getprop (name);
+ if (rv)
+ return (rv);
+ else
+ return (def);
+}
+
+/*
+ * addprop
+ */
+
+void addprop (char *name, char *value)
+{
+ unsigned char *cp;
+ unsigned long hash=0;
+ prop_t **bpp;
+ prop_t *bp;
+ int i=0;
+
+ bp = (prop_t *)malloc (sizeof (prop_t));
+
+ bp->next = 0;
+ bp->name = sxerox (name);
+ bp->value = sxerox (value);
+
+ for (cp = (unsigned char *)name; *cp; cp++)
+ hash ^= (*cp)<<(hash_shifts[(i++)&0x3]);
+
+ bpp = &buckets [hash%NBUCKETS];
+
+ if (*bpp == NULL)
+ *bpp = bp;
+ else {
+ bp->next = *bpp;
+ *bpp = bp;
+ }
+}
+
+/*
+ * sxerox
+ */
+
+static char *sxerox (char *s)
+{
+ char *rv = (char *) malloc (strlen (s) + 1);
+ strcpy (rv, s);
+ return rv;
+}
+
+/*
+ * readprops
+ */
+
+#define START 0
+#define READNAME 1
+#define READVALUE 2
+#define C_COMMENT 3
+#define CPP_COMMENT 4
+
+int readprops (char *filename)
+{
+ FILE *ifp;
+ unsigned char c;
+ int state=START;
+ int linenum=1;
+ char namebuf [128];
+ char valbuf [512];
+ int i;
+
+ ifp = fopen (filename, "r");
+
+ if (ifp == NULL)
+ return (-1);
+
+ while (1) {
+
+ readchar:
+ c = getc (ifp);
+
+ again:
+ switch (state) {
+ case START:
+ if (feof (ifp)) {
+ fclose (ifp);
+ return (0);
+ }
+
+ if (c == ' ' || c == '\t')
+ goto readchar;
+
+ if (c == '\n') {
+ linenum++;
+ goto readchar;
+ }
+ if (isalpha (c) || (c == '_')) {
+ state = READNAME;
+ goto again;
+ }
+ if (c == '/') {
+ c = getc (ifp);
+ if (c == '/') {
+ state = CPP_COMMENT;
+ goto readchar;
+ } else if (c == '*') {
+ state = C_COMMENT;
+ goto readchar;
+ } else {
+ fprintf (stderr, "unknown token '/' line %d\n",
+ linenum);
+ exit(1);
+ }
+ }
+ fprintf (stderr, "unknown token '%c' line %d\n",
+ c, linenum);
+ exit (1);
+ break;
+
+ case CPP_COMMENT:
+ while (1) {
+ c = getc (ifp);
+ if (feof (ifp))
+ return (0);
+ if (c == '\n') {
+ linenum++;
+ state = START;
+ goto readchar;
+ }
+ }
+ break;
+
+ case C_COMMENT:
+ while (1) {
+ c = getc (ifp);
+ if (feof (ifp)) {
+ fprintf (stderr, "unterminated comment, line %d\n",
+ linenum);
+ exit (1);
+ }
+ if (c == '*') {
+ staragain:
+ c = getc (ifp);
+ if (c == '/') {
+ state = START;
+ goto readchar;
+ }
+ if (c == '*')
+ goto staragain;
+ }
+ }
+ break;
+
+ case READNAME:
+ i = 0;
+ namebuf[i++] = c;
+ while (1) {
+ c = getc (ifp);
+ if (feof (ifp)) {
+ fprintf (stderr, "EOF while reading a name, line %d\n",
+ linenum);
+ exit (1);
+ }
+ if ((!isalnum (c)) && (c != '_')) {
+ namebuf [i] = 0;
+ state = READVALUE;
+ goto again;
+ }
+ namebuf [i++] = c;
+ }
+ break;
+
+ case READVALUE:
+ i = 0;
+ while ((c == ' ') || (c == '\t') || (c == '=')) {
+ c = getc (ifp);
+ if (feof (ifp)) {
+ fprintf (stderr, "EOF while reading a value, line %d\n",
+ linenum);
+ exit (1);
+ }
+ }
+ goto firsttime;
+ while (1) {
+ c = getc (ifp);
+
+ firsttime:
+ if (c == '\\') {
+ c = getc (ifp);
+ if (feof (ifp)) {
+ fprintf (stderr, "EOF after '\\', line %d\n",
+ linenum);
+ exit (1);
+ }
+ valbuf[i++] = c;
+ continue;
+ }
+ if (c == '\n') {
+ linenum++;
+ while (valbuf [i-1] == ' ' || valbuf[i-1] == '\t')
+ i--;
+ valbuf[i] = 0;
+ addprop (namebuf, valbuf);
+ state = START;
+ goto readchar;
+ }
+ valbuf[i++] = c;
+ }
+
+ }
+ }
+}