aboutsummaryrefslogtreecommitdiffstats
path: root/src/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/elftool/dir.dox19
-rw-r--r--src/tools/elftool/elftool.c464
-rw-r--r--src/tools/g2/clib.c157
-rw-r--r--src/tools/g2/cpel.c470
-rw-r--r--src/tools/g2/cpel.h83
-rw-r--r--src/tools/g2/events.c475
-rw-r--r--src/tools/g2/g2.h196
-rw-r--r--src/tools/g2/g2version.c19
-rw-r--r--src/tools/g2/main.c199
-rw-r--r--src/tools/g2/menu1.c565
-rw-r--r--src/tools/g2/mkversion.c77
-rw-r--r--src/tools/g2/pointsel.c854
-rw-r--r--src/tools/g2/props.c279
-rw-r--r--src/tools/g2/props.h21
-rw-r--r--src/tools/g2/view1.c3237
-rw-r--r--src/tools/perftool/c2cpel.c251
-rw-r--r--src/tools/perftool/cpel.h83
-rw-r--r--src/tools/perftool/cpel_util.c456
-rw-r--r--src/tools/perftool/cpel_util.h68
-rw-r--r--src/tools/perftool/cpelatency.c927
-rw-r--r--src/tools/perftool/cpeldump.c641
-rw-r--r--src/tools/perftool/cpelinreg.c892
-rw-r--r--src/tools/perftool/cpelstate.c822
-rw-r--r--src/tools/perftool/delsvec.c315
-rw-r--r--src/tools/perftool/elog_merge.c181
-rw-r--r--src/tools/perftool/linreg.c78
-rw-r--r--src/tools/perftool/new.cpelbin0 -> 1672 bytes
-rw-r--r--src/tools/perftool/new.elogbin0 -> 4525 bytes
-rw-r--r--src/tools/perftool/props.c280
-rw-r--r--src/tools/vppapigen/gram.y91
-rw-r--r--src/tools/vppapigen/lex.c1120
-rw-r--r--src/tools/vppapigen/lex.h51
-rw-r--r--src/tools/vppapigen/node.c1547
-rw-r--r--src/tools/vppapigen/node.h96
34 files changed, 15014 insertions, 0 deletions
diff --git a/src/tools/elftool/dir.dox b/src/tools/elftool/dir.dox
new file mode 100644
index 00000000..40426e04
--- /dev/null
+++ b/src/tools/elftool/dir.dox
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Copyright (c) 2016 Comcast Cable Communications Management, LLC.
+ *
+ * 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.
+ */
+/** @dir
+ * @brief VPP instrastructure tools.
+ */
diff --git a/src/tools/elftool/elftool.c b/src/tools/elftool/elftool.c
new file mode 100644
index 00000000..d9d3704b
--- /dev/null
+++ b/src/tools/elftool/elftool.c
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 2015 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.
+ */
+/*
+ Copyright (c) 2008 Eliot Dresselhaus
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <vppinfra/elf.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifndef CLIB_UNIX
+#error "unix only"
+#endif
+
+typedef struct {
+ elf_main_t elf_main;
+ char * input_file;
+ char * output_file;
+ char * set_interpreter;
+ char * set_rpath;
+ int unset_rpath;
+ int verbose;
+ int quiet;
+ int allow_elf_shared;
+ /* for use in the optimized / simplified case */
+ u64 file_size;
+ u64 interpreter_offset;
+ u64 rpath_offset;
+} elf_tool_main_t;
+
+static clib_error_t * elf_set_interpreter (elf_main_t * em,
+ elf_tool_main_t * tm)
+{
+ elf_segment_t * g;
+ elf_section_t * s;
+ clib_error_t * error;
+ char * interp = tm->set_interpreter;
+
+ switch (em->first_header.file_type)
+ {
+ case ELF_EXEC:
+ break;
+
+ case ELF_SHARED:
+ if (tm->allow_elf_shared)
+ break;
+ /* Note flowthrough */
+ default:
+ return clib_error_return (0, "unacceptable file_type");
+ }
+
+ vec_foreach (g, em->segments)
+ {
+ if (g->header.type == ELF_SEGMENT_INTERP)
+ break;
+ }
+
+ if (g >= vec_end (em->segments))
+ return clib_error_return (0, "interpreter not found");
+
+ if (g->header.memory_size < 1 + strlen (interp))
+ return clib_error_return (0, "given interpreter does not fit; must be less than %d bytes (`%s' given)",
+ g->header.memory_size, interp);
+
+ error = elf_get_section_by_start_address (em, g->header.virtual_address, &s);
+ if (error)
+ return error;
+
+ /* Put in new null terminated string. */
+ memset (s->contents, 0, vec_len (s->contents));
+ clib_memcpy (s->contents, interp, strlen (interp));
+
+ return 0;
+}
+
+static void
+delete_rpath_for_section (elf_main_t * em, elf_section_t * s)
+{
+ elf64_dynamic_entry_t * e;
+ elf64_dynamic_entry_t * new_es = 0;
+
+ vec_foreach (e, em->dynamic_entries)
+ {
+ switch (e->type)
+ {
+ case ELF_DYNAMIC_ENTRY_RPATH:
+ case ELF_DYNAMIC_ENTRY_RUN_PATH:
+ break;
+
+ default:
+ vec_add1 (new_es, e[0]);
+ break;
+ }
+ }
+
+ /* Pad so as to keep section size constant. */
+ {
+ elf64_dynamic_entry_t e_end;
+ e_end.type = ELF_DYNAMIC_ENTRY_END;
+ e_end.data = 0;
+ while (vec_len (new_es) < vec_len (em->dynamic_entries))
+ vec_add1 (new_es, e_end);
+ }
+
+ vec_free (em->dynamic_entries);
+ em->dynamic_entries = new_es;
+
+ elf_set_dynamic_entries (em);
+}
+
+static void delete_rpath (elf_main_t * em)
+{
+ elf_section_t * s;
+
+ vec_foreach (s, em->sections)
+ {
+ switch (s->header.type)
+ {
+ case ELF_SECTION_DYNAMIC:
+ delete_rpath_for_section (em, s);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static clib_error_t *
+set_rpath_for_section (elf_main_t * em, elf_section_t * s, char * new_rpath)
+{
+ elf64_dynamic_entry_t * e;
+ char * old_rpath;
+ int old_len, new_len = strlen (new_rpath);
+ u8 * new_string_table = vec_dup (em->dynamic_string_table);
+
+ vec_foreach (e, em->dynamic_entries)
+ {
+ switch (e->type)
+ {
+ case ELF_DYNAMIC_ENTRY_RPATH:
+ case ELF_DYNAMIC_ENTRY_RUN_PATH:
+ old_rpath = (char *) new_string_table + e->data;
+ old_len = strlen (old_rpath);
+ if (old_len < new_len)
+ return clib_error_return (0, "rpath of `%s' does not fit (old rpath `%s')",
+ new_rpath, old_rpath);
+ strcpy (old_rpath, new_rpath);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ elf_set_section_contents (em, em->dynamic_string_table_section_index,
+ new_string_table,
+ vec_bytes (new_string_table));
+
+ return 0;
+}
+
+static clib_error_t *
+set_rpath (elf_main_t * em, char * rpath)
+{
+ clib_error_t * error = 0;
+ elf_section_t * s;
+
+ vec_foreach (s, em->sections)
+ {
+ switch (s->header.type)
+ {
+ case ELF_SECTION_DYNAMIC:
+ error = set_rpath_for_section (em, s, rpath);
+ if (error)
+ return error;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return error;
+}
+
+static clib_error_t *
+set_interpreter_rpath (elf_tool_main_t * tm)
+{
+ int ifd = -1, ofd = -1;
+ struct stat fd_stat;
+ u8 *idp = 0; /* warning be gone */
+ u64 mmap_length = 0, i;
+ u32 run_length;
+ u8 in_run;
+ u64 offset0 = 0, offset1 = 0;
+ clib_error_t * error = 0;
+ int fix_in_place = 0;
+
+ if (!strcmp (tm->input_file, tm->output_file))
+ fix_in_place = 1;
+
+ ifd = open (tm->input_file, O_RDWR);
+ if (ifd < 0)
+ {
+ error = clib_error_return_unix (0, "open `%s'", tm->input_file);
+ goto done;
+ }
+
+ if (fstat (ifd, &fd_stat) < 0)
+ {
+ error = clib_error_return_unix (0, "fstat `%s'", tm->input_file);
+ goto done;
+ }
+
+ if (!(fd_stat.st_mode & S_IFREG))
+ {
+ error = clib_error_return (0, "%s is not a regular file", tm->input_file);
+ goto done;
+ }
+
+ mmap_length = fd_stat.st_size;
+ if (mmap_length < 4)
+ {
+ error = clib_error_return (0, "%s too short", tm->input_file);
+ goto done;
+ }
+
+ /* COW-mapping, since we intend to write the fixups */
+ if (fix_in_place)
+ idp = mmap (0, mmap_length, PROT_READ | PROT_WRITE, MAP_SHARED,
+ ifd, /* offset */ 0);
+ else
+ idp = mmap (0, mmap_length, PROT_READ | PROT_WRITE, MAP_PRIVATE,
+ ifd, /* offset */ 0);
+ if (~pointer_to_uword (idp) == 0)
+ {
+ mmap_length = 0;
+ error = clib_error_return_unix (0, "mmap `%s'", tm->input_file);
+ goto done;
+ }
+
+ if (idp[0] != 0x7f || idp[1] != 'E' || idp[2] != 'L' || idp[3] != 'F')
+ {
+ error = clib_error_return (0, "not an ELF file '%s'", tm->input_file);
+ goto done;
+ }
+
+ in_run = 0;
+ run_length = 0;
+
+ for (i = 0; i < mmap_length; i++)
+ {
+ if (idp[i] == '/')
+ {
+ if (in_run)
+ run_length++;
+ else
+ {
+ in_run = 1;
+ run_length = 1;
+ }
+ }
+ else
+ {
+ if (in_run && run_length >= 16)
+ {
+ if (offset0 == 0)
+ offset0 = (i - run_length);
+ else if (offset1 == 0)
+ {
+ offset1 = (i - run_length);
+ goto found_both;
+ }
+ }
+ in_run = 0;
+ run_length = 0;
+ }
+ }
+
+ if (offset0 == 0)
+ {
+ error = clib_error_return (0, "no fixup markers in %s",
+ tm->input_file);
+ goto done;
+ }
+
+ found_both:
+ if (0)
+ clib_warning ("offset0 %lld (0x%llx), offset1 %lld (0x%llx)",
+ offset0, offset0, offset1, offset1);
+
+ /* Executable file case */
+ if (offset0 && offset1)
+ {
+ tm->interpreter_offset = offset0;
+ tm->rpath_offset = offset1;
+ }
+ else /* shared library case */
+ {
+ tm->interpreter_offset = 0;
+ tm->rpath_offset = offset0;
+ }
+
+ if (tm->interpreter_offset)
+ clib_memcpy (&idp[tm->interpreter_offset], tm->set_interpreter,
+ strlen (tm->set_interpreter)+1);
+
+ if (tm->rpath_offset)
+ clib_memcpy (&idp[tm->rpath_offset], tm->set_rpath,
+ strlen (tm->set_rpath)+1);
+
+ /* Write the output file... */
+ if (fix_in_place == 0)
+ {
+ ofd = open (tm->output_file, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (ofd < 0)
+ {
+ error = clib_error_return_unix (0, "create `%s'", tm->output_file);
+ goto done;
+ }
+
+ if (write (ofd, idp, mmap_length) != mmap_length)
+ error = clib_error_return_unix (0, "write `%s'", tm->output_file);
+ }
+
+ done:
+ if (mmap_length > 0 && idp)
+ munmap (idp, mmap_length);
+ if (ifd >= 0)
+ close (ifd);
+ if (ofd >= 0)
+ close (ofd);
+ return error;
+}
+
+
+int main (int argc, char * argv[])
+{
+ elf_tool_main_t _tm, * tm = &_tm;
+ elf_main_t * em = &tm->elf_main;
+ unformat_input_t i;
+ clib_error_t * error = 0;
+
+ memset (tm, 0, sizeof (tm[0]));
+ unformat_init_command_line (&i, argv);
+
+ while (unformat_check_input (&i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (&i, "in %s", &tm->input_file))
+ ;
+ else if (unformat (&i, "out %s", &tm->output_file))
+ ;
+ else if (unformat (&i, "set-interpreter %s", &tm->set_interpreter))
+ ;
+ else if (unformat (&i, "set-rpath %s", &tm->set_rpath))
+ ;
+ else if (unformat (&i, "unset-rpath"))
+ tm->unset_rpath = 1;
+ else if (unformat (&i, "verbose"))
+ tm->verbose = ~0;
+ else if (unformat (&i, "verbose-symbols"))
+ tm->verbose |= FORMAT_ELF_MAIN_SYMBOLS;
+ else if (unformat (&i, "verbose-relocations"))
+ tm->verbose |= FORMAT_ELF_MAIN_RELOCATIONS;
+ else if (unformat (&i, "verbose-dynamic"))
+ tm->verbose |= FORMAT_ELF_MAIN_DYNAMIC;
+ else if (unformat (&i, "quiet"))
+ tm->quiet = 1;
+ else if (unformat (&i, "allow-elf-shared"))
+ tm->allow_elf_shared = 1;
+ else
+ {
+ error = unformat_parse_error (&i);
+ goto done;
+ }
+ }
+
+ if (! tm->input_file)
+ {
+ error = clib_error_return (0, "no input file");
+ goto done;
+ }
+
+ /* Do the typical case a stone-simple way... */
+ if (tm->quiet && tm->set_interpreter && tm->set_rpath && tm->output_file)
+ {
+ error = set_interpreter_rpath (tm);
+ goto done;
+ }
+
+ error = elf_read_file (em, tm->input_file);
+
+ if (error)
+ goto done;
+
+ if (tm->verbose)
+ fformat (stdout, "%U", format_elf_main, em, tm->verbose);
+
+ if (tm->set_interpreter)
+ {
+ error = elf_set_interpreter (em, tm);
+ if (error)
+ goto done;
+ }
+
+ if (tm->set_rpath)
+ {
+ error = set_rpath (em, tm->set_rpath);
+ if (error)
+ goto done;
+ }
+
+ if (tm->unset_rpath)
+ delete_rpath (em);
+
+ if (tm->output_file)
+ error = elf_write_file (em, tm->output_file);
+
+ elf_main_free (em);
+
+ done:
+ if (error)
+ {
+ if (tm->quiet == 0)
+ clib_error_report (error);
+ return 1;
+ }
+ else
+ return 0;
+}
diff --git a/src/tools/g2/clib.c b/src/tools/g2/clib.c
new file mode 100644
index 00000000..845026b6
--- /dev/null
+++ b/src/tools/g2/clib.c
@@ -0,0 +1,157 @@
+/*
+ *------------------------------------------------------------------
+ * 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 = (u8 *) 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();
+
+ 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);
+}
+
+unsigned int vl(void *a)
+{
+ return vec_len (a);
+}
diff --git a/src/tools/g2/cpel.c b/src/tools/g2/cpel.c
new file mode 100644
index 00000000..8bcc91e6
--- /dev/null
+++ b/src/tools/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/src/tools/g2/cpel.h b/src/tools/g2/cpel.h
new file mode 100644
index 00000000..73e4aea5
--- /dev/null
+++ b/src/tools/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/src/tools/g2/events.c b/src/tools/g2/events.c
new file mode 100644
index 00000000..6839a435
--- /dev/null
+++ b/src/tools/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_malloc0(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_malloc0(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/src/tools/g2/g2.h b/src/tools/g2/g2.h
new file mode 100644
index 00000000..f1f268a8
--- /dev/null
+++ b/src/tools/g2/g2.h
@@ -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.
+ */
+
+/*
+ * 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;
+ int selected;
+} 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/src/tools/g2/g2version.c b/src/tools/g2/g2version.c
new file mode 100644
index 00000000..4b6f9313
--- /dev/null
+++ b/src/tools/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/src/tools/g2/main.c b/src/tools/g2/main.c
new file mode 100644
index 00000000..1ec7983a
--- /dev/null
+++ b/src/tools/g2/main.c
@@ -0,0 +1,199 @@
+/*
+ *------------------------------------------------------------------
+ * 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>
+#include <vppinfra/mem.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;
+
+ clib_mem_init (0, ((uword)3<<30));
+
+ 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/src/tools/g2/menu1.c b/src/tools/g2/menu1.c
new file mode 100644
index 00000000..fce81fa6
--- /dev/null
+++ b/src/tools/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/src/tools/g2/mkversion.c b/src/tools/g2/mkversion.c
new file mode 100644
index 00000000..3523fbe6
--- /dev/null
+++ b/src/tools/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/src/tools/g2/pointsel.c b/src/tools/g2/pointsel.c
new file mode 100644
index 00000000..018dc213
--- /dev/null
+++ b/src/tools/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/src/tools/g2/props.c b/src/tools/g2/props.c
new file mode 100644
index 00000000..a23dc050
--- /dev/null
+++ b/src/tools/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/src/tools/g2/props.h b/src/tools/g2/props.h
new file mode 100644
index 00000000..6289941d
--- /dev/null
+++ b/src/tools/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/src/tools/g2/view1.c b/src/tools/g2/view1.c
new file mode 100644
index 00000000..c5f799dc
--- /dev/null
+++ b/src/tools/g2/view1.c
@@ -0,0 +1,3237 @@
+/*
+ *------------------------------------------------------------------
+ * 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 */
+/* color format: 0 (for static colors), r (0-64k), g (0-64k), b (0-64k) */
+GdkColor fg_black = {0, 0, 0, 0};
+GdkColor fg_red = {0, 65535, 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 monochrome 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,
+ SLEW_LEFT_BUTTON,
+ SLEW_RIGHT_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 */
+ double last_time_interval; /* last time interval, in f64 seconds */
+
+ /* 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_time_slew_right_button;
+static GtkWidget *s_view1_time_slew_left_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)
+{
+ pid_sort_t *psp;
+
+ psp = (g_pids + pid_index);
+
+ if (psp->selected)
+ gdk_gc_set_foreground(da->style->black_gc, &s_color[0]);
+ else 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 int 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 0;
+
+ time_per_pixel = dtime_per_pixel(vp);
+
+ start_index = find_event_index (vp->minvistime);
+
+ /* Too far right? */
+ if (start_index >= g_nevents)
+ return 0;
+
+ /*
+ * 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();
+ return 0;
+ }
+ }
+
+ 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();
+ return 0;
+ } 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;
+ }
+ return 0;
+ }
+ ep++;
+ }
+ return -1;
+}
+
+/****************************************************************************
+* toggle_track_select
+****************************************************************************/
+
+static void toggle_track_select (GdkEventButton *event,
+ v1_geometry_t *vp)
+{
+ int i;
+ int pid_index;
+ int y, delta_y;
+ pid_sort_t *psp;
+
+ 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;
+ psp = (g_pids + pid_index);
+ psp->selected ^= 1;
+ view1_display_when_idle();
+}
+
+/****************************************************************************
+* deselect_tracks
+****************************************************************************/
+static void deselect_tracks (void)
+{
+ int i;
+
+ for (i = 0; i < g_npids; i++)
+ g_pids[i].selected = 0;
+
+}
+
+
+/****************************************************************************
+* 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_malloc0(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 / select track */
+ if (toggle_event_select(event, s_v1))
+ toggle_track_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);
+ }
+ s_v1->last_time_interval = 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+1));
+ }
+
+ /*
+ * Go through and assign a color for each track.
+ */
+ /* Setup entry 0 in the colormap as pure red (for selection) */
+ s_color[0] = fg_red;
+
+ for (i = 1; 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+1, 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_malloc0(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);
+}
+
+int event_time_cmp (const void *a, const void *b)
+{
+ const event_t *e1 = a;
+ const event_t *e2 = b;
+
+ if (e1->time < e2->time)
+ return -1;
+ else if (e1->time > e2->time)
+ return 1;
+ return 0;
+}
+
+/****************************************************************************
+* slew_tracks
+****************************************************************************/
+static void slew_tracks (v1_geometry_t *vp, enum view1_button_click which)
+{
+ event_t *ep;
+ pid_sort_t *pp;
+ int pid_index;
+ ulonglong delta;
+
+ delta = (ulonglong) (vp->last_time_interval);
+
+ /* Make sure we don't push events to the left of the big bang */
+ if (which == SLEW_LEFT_BUTTON) {
+ for (ep = g_events; ep < (g_events + g_nevents); ep++) {
+ pid_index = ep->pid->pid_index;
+ pp = (g_pids + pid_index);
+
+ if (pp->selected) {
+ if (ep->time < delta) {
+ infobox("Slew Range Error",
+ "\nCan't slew selected data left that far..."
+ "\nEvents would preceed the Big Bang (t=0)...");
+ goto out;
+ }
+ }
+ }
+ }
+
+ for (ep = g_events; ep < (g_events + g_nevents); ep++) {
+ pid_index = ep->pid->pid_index;
+ pp = (g_pids + pid_index);
+
+ if (pp->selected) {
+ if (which == SLEW_LEFT_BUTTON)
+ ep->time -= delta;
+ else
+ ep->time += delta;
+ }
+ }
+
+ /* Re-sort the events, to avoid screwing up the event display */
+ qsort (g_events, g_nevents, sizeof(event_t), event_time_cmp);
+
+ /* De-select tracks */
+ deselect_tracks();
+
+out:
+ view1_display_when_idle();
+}
+
+/****************************************************************************
+* 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;
+
+ case SLEW_LEFT_BUTTON:
+ case SLEW_RIGHT_BUTTON:
+ if (s_v1->last_time_interval < 10e-9) {
+ infobox("slew", "\nNo time interval set...\n");
+ break;
+ }
+ slew_tracks (s_v1, click);
+ 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");
+
+ s_view1_time_slew_left_button = gtk_button_new_with_label("<-TimeSlew");
+ s_view1_time_slew_right_button = gtk_button_new_with_label("TimeSlew->");
+
+ 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_signal_connect (GTK_OBJECT(s_view1_time_slew_left_button), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) SLEW_LEFT_BUTTON);
+
+ gtk_signal_connect (GTK_OBJECT(s_view1_time_slew_right_button), "clicked",
+ GTK_SIGNAL_FUNC(view1_button_click_callback),
+ (gpointer) SLEW_RIGHT_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);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2),
+ s_view1_time_slew_left_button,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2),
+ s_view1_time_slew_right_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;
+
+ pp = (g_pids + pid_index);
+
+ set_color(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/src/tools/perftool/c2cpel.c b/src/tools/perftool/c2cpel.c
new file mode 100644
index 00000000..379c2bc6
--- /dev/null
+++ b/src/tools/perftool/c2cpel.c
@@ -0,0 +1,251 @@
+/*
+ *------------------------------------------------------------------
+ * 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 <vppinfra/mem.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;
+
+ clib_mem_init (0, ((uword)3<<30));
+
+ 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/src/tools/perftool/cpel.h b/src/tools/perftool/cpel.h
new file mode 100644
index 00000000..0bfb1a68
--- /dev/null
+++ b/src/tools/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/src/tools/perftool/cpel_util.c b/src/tools/perftool/cpel_util.c
new file mode 100644
index 00000000..7ee9b6e2
--- /dev/null
+++ b/src/tools/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/src/tools/perftool/cpel_util.h b/src/tools/perftool/cpel_util.h
new file mode 100644
index 00000000..b76f7a4b
--- /dev/null
+++ b/src/tools/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/src/tools/perftool/cpelatency.c b/src/tools/perftool/cpelatency.c
new file mode 100644
index 00000000..7b87d606
--- /dev/null
+++ b/src/tools/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 *) = 0;
+
+ 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/src/tools/perftool/cpeldump.c b/src/tools/perftool/cpeldump.c
new file mode 100644
index 00000000..be0a70df
--- /dev/null
+++ b/src/tools/perftool/cpeldump.c
@@ -0,0 +1,641 @@
+/*
+ *------------------------------------------------------------------
+ * 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/mem.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;
+
+ clib_mem_init (0, ((uword)3<<30));
+
+ 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/src/tools/perftool/cpelinreg.c b/src/tools/perftool/cpelinreg.c
new file mode 100644
index 00000000..115afad7
--- /dev/null
+++ b/src/tools/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/src/tools/perftool/cpelstate.c b/src/tools/perftool/cpelstate.c
new file mode 100644
index 00000000..3fd9ccb9
--- /dev/null
+++ b/src/tools/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/src/tools/perftool/delsvec.c b/src/tools/perftool/delsvec.c
new file mode 100644
index 00000000..724935d3
--- /dev/null
+++ b/src/tools/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/src/tools/perftool/elog_merge.c b/src/tools/perftool/elog_merge.c
new file mode 100644
index 00000000..46b19dd5
--- /dev/null
+++ b/src/tools/perftool/elog_merge.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2015 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.
+ */
+/*
+ Copyright (c) 2005 Eliot Dresselhaus
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <vppinfra/elog.h>
+#include <vppinfra/error.h>
+#include <vppinfra/format.h>
+#include <vppinfra/random.h>
+#include <vppinfra/serialize.h>
+#include <vppinfra/unix.h>
+#include <vppinfra/pool.h>
+#include <vppinfra/hash.h>
+
+int
+elog_merge_main (unformat_input_t * input)
+{
+ clib_error_t *error = 0;
+ elog_main_t _em, *em = &_em;
+ u32 verbose;
+ char *dump_file, *merge_file, **merge_files;
+ u8 *tag, **tags;
+ f64 align_tweak;
+ f64 *align_tweaks;
+ uword i;
+ elog_main_t *ems;
+
+ verbose = 0;
+ dump_file = 0;
+ merge_files = 0;
+ tags = 0;
+ align_tweaks = 0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "dump %s", &dump_file))
+ ;
+ else if (unformat (input, "tag %s", &tag))
+ vec_add1 (tags, tag);
+ else if (unformat (input, "merge %s", &merge_file))
+ vec_add1 (merge_files, merge_file);
+
+ else if (unformat (input, "verbose %=", &verbose, 1))
+ ;
+ else if (unformat (input, "align-tweak %f", &align_tweak))
+ vec_add1 (align_tweaks, align_tweak);
+ else
+ {
+ error = clib_error_create ("unknown input `%U'\n",
+ format_unformat_error, input);
+ goto done;
+ }
+ }
+
+ vec_clone (ems, merge_files);
+
+ /* Supply default tags as needed */
+ if (vec_len (tags) < vec_len (ems))
+ {
+ for (i = vec_len (tags); i < vec_len (ems); i++)
+ vec_add1 (tags, format (0, "F%d%c", i, 0));
+ }
+
+ for (i = 0; i < vec_len (ems); i++)
+ {
+ if ((error = elog_read_file ((i == 0) ? em : &ems[i], merge_files[i])))
+ goto done;
+ if (i > 0)
+ {
+ align_tweak = 0.0;
+ if (i <= vec_len (align_tweaks))
+ align_tweak = align_tweaks[i - 1];
+ elog_merge (em, tags[0], &ems[i], tags[i], align_tweak);
+ tags[0] = 0;
+ }
+ }
+
+ if (dump_file)
+ {
+ if ((error =
+ elog_write_file (em, dump_file, 0 /* do not flush ring */ )))
+ goto done;
+ }
+
+ if (verbose)
+ {
+ elog_event_t *e, *es;
+ es = elog_get_events (em);
+ vec_foreach (e, es)
+ {
+ clib_warning ("%18.9f: %12U %U\n", e->time,
+ format_elog_track, em, e, format_elog_event, em, e);
+ }
+ }
+
+done:
+ if (error)
+ clib_error_report (error);
+ return 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+ unformat_input_t i;
+ int r;
+
+ clib_mem_init (0, 3ULL << 30);
+
+ unformat_init_command_line (&i, argv);
+ r = elog_merge_main (&i);
+ unformat_free (&i);
+ return r;
+}
+
+/*
+ * GDB callable function: vl - Return vector length of vector
+ */
+u32
+vl (void *p)
+{
+ return vec_len (p);
+}
+
+/*
+ * GDB callable function: pe - call pool_elts - number of elements in a pool
+ */
+uword
+pe (void *v)
+{
+ return (pool_elts (v));
+}
+
+/*
+ * GDB callable function: he - call hash_elts - number of elements in a hash
+ */
+uword
+he (void *v)
+{
+ return (hash_elts (v));
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/tools/perftool/linreg.c b/src/tools/perftool/linreg.c
new file mode 100644
index 00000000..084091bb
--- /dev/null
+++ b/src/tools/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/src/tools/perftool/new.cpel b/src/tools/perftool/new.cpel
new file mode 100644
index 00000000..b0f35958
--- /dev/null
+++ b/src/tools/perftool/new.cpel
Binary files differ
diff --git a/src/tools/perftool/new.elog b/src/tools/perftool/new.elog
new file mode 100644
index 00000000..2d99bb16
--- /dev/null
+++ b/src/tools/perftool/new.elog
Binary files differ
diff --git a/src/tools/perftool/props.c b/src/tools/perftool/props.c
new file mode 100644
index 00000000..84af5b1c
--- /dev/null
+++ b/src/tools/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;
+ }
+
+ }
+ }
+}
diff --git a/src/tools/vppapigen/gram.y b/src/tools/vppapigen/gram.y
new file mode 100644
index 00000000..52bb65c5
--- /dev/null
+++ b/src/tools/vppapigen/gram.y
@@ -0,0 +1,91 @@
+%{
+/*
+ * gram.y - message definition language
+ *
+ * Copyright (c) 2009 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 void yyerror (char *s);
+extern int yylex (void);
+
+#define YYSTYPE void *
+
+void generate (YYSTYPE);
+ YYSTYPE add_slist(YYSTYPE, YYSTYPE);
+ YYSTYPE add_define(YYSTYPE, YYSTYPE);
+ YYSTYPE suppress_version(void);
+ YYSTYPE add_defbody(YYSTYPE, YYSTYPE);
+ YYSTYPE add_primtype(YYSTYPE, YYSTYPE, YYSTYPE);
+ YYSTYPE add_complex(YYSTYPE, YYSTYPE);
+ YYSTYPE add_union(YYSTYPE, YYSTYPE);
+ YYSTYPE add_scalar_vbl(YYSTYPE);
+ YYSTYPE add_vector_vbl(YYSTYPE, YYSTYPE);
+ YYSTYPE add_variable_length_vector_vbl(YYSTYPE, YYSTYPE);
+ YYSTYPE set_flags(YYSTYPE, YYSTYPE);
+%}
+
+%token NAME RPAR LPAR SEMI LBRACK RBRACK NUMBER PRIMTYPE BARF
+%token TPACKED DEFINE LCURLY RCURLY STRING UNION
+%token HELPER_STRING COMMA
+%token NOVERSION MANUAL_PRINT MANUAL_ENDIAN TYPEONLY DONT_TRACE AUTOREPLY
+
+%%
+
+pgm: slist {generate ($1);}
+ ;
+
+slist: slist stmt {$$ = add_slist ($1, $2);}
+ | stmt {$$ = $1;}
+ ;
+
+stmt: flist defn {$$ = set_flags($1, $2);}
+ | defn {$$ = $1;}
+ ;
+
+flist: flist flag {$$ = (YYSTYPE)(unsigned long)
+ ((unsigned long) $1
+ | (unsigned long) $2);}
+ | flag {$$ = $1;}
+ ;
+
+flag:
+ MANUAL_PRINT {$$ = $1;}
+ | MANUAL_ENDIAN {$$ = $1;}
+ | DONT_TRACE {$$ = $1;}
+ | TYPEONLY {$$ = $1;}
+ | AUTOREPLY {$$ = $1;}
+ ;
+
+defn: DEFINE NAME LCURLY defbody RCURLY SEMI
+ {$$ = add_define($2, $4);}
+
+ | NOVERSION SEMI
+ {$$ = suppress_version();}
+ ;
+
+defbody: defbody onedef {$$ = add_defbody($1, $2);}
+ | onedef {$$ = $1;}
+ ;
+
+onedef: PRIMTYPE vbl SEMI {$$ = add_primtype($1, $2, 0);}
+ | TPACKED PRIMTYPE vbl SEMI {$$ = add_primtype($1, $2, $3);}
+ | NAME vbl SEMI {$$ = add_complex($1, $2);}
+ | UNION NAME LCURLY defbody RCURLY SEMI
+ {$$ = add_union($2, $4);}
+ ;
+
+vbl: NAME {$$ = add_scalar_vbl($1);}
+ | NAME LBRACK NUMBER RBRACK {$$ = add_vector_vbl($1, $3);}
+ | NAME LBRACK NAME RBRACK {$$ = add_variable_length_vector_vbl($1, $3);}
+ ;
diff --git a/src/tools/vppapigen/lex.c b/src/tools/vppapigen/lex.c
new file mode 100644
index 00000000..e6358143
--- /dev/null
+++ b/src/tools/vppapigen/lex.c
@@ -0,0 +1,1120 @@
+/*
+ *------------------------------------------------------------------
+ * lex.c - API generator lexical analyzer
+ *
+ * Copyright (c) 1996-2009 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 <time.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "lex.h"
+#include "node.h"
+#include "tools/vppapigen/gram.h"
+#include <vppinfra/clib.h>
+#include <vppinfra/fifo.h>
+#include <vppinfra/format.h>
+
+FILE *ifp, *ofp, *pythonfp, *jsonfp;
+char *vlib_app_name = "vpp";
+int dump_tree;
+time_t starttime;
+char *input_filename;
+char *current_filename;
+int current_filename_allocated;
+unsigned long input_crc;
+unsigned long message_crc;
+int yydebug;
+char *push_input_fifo;
+char saved_ungetc_char;
+char have_ungetc_char;
+
+/*
+ * lexer variable definitions
+ */
+
+static const char *version = "0.1";
+static int the_lexer_linenumber = 1;
+static enum lex_state the_lexer_state = START_STATE;
+
+/*
+ * private prototypes
+ */
+static void usage (char *);
+static int name_check (const char *, YYSTYPE *);
+static int name_compare (const char *, const char *);
+extern int yydebug;
+extern YYSTYPE yylval;
+
+unsigned int crc32c_table[256] = {
+ 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
+ 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
+ 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
+ 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
+ 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
+ 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
+ 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
+ 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
+ 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
+ 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
+ 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
+ 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
+ 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
+ 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
+ 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
+ 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
+ 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
+ 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
+ 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
+ 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
+ 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
+ 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
+ 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
+ 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
+ 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
+ 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
+ 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
+ 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
+ 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
+ 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
+ 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
+ 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
+ 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
+ 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
+ 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
+ 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
+ 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
+ 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
+ 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
+ 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
+ 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
+ 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
+ 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
+ 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
+ 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
+ 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
+ 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
+ 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
+ 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
+ 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
+ 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
+ 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
+ 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
+ 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
+ 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
+ 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
+ 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
+ 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
+ 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
+ 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
+ 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
+ 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
+ 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
+ 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351
+};
+
+static inline unsigned long CRC8 (unsigned long crc,
+ unsigned char d)
+{
+ return ((crc >> 8) ^ crc32c_table[(crc ^ d) & 0xFF]);
+}
+static inline unsigned long CRC16 (unsigned long crc,
+ unsigned short d)
+{
+ crc = CRC8 (crc, d & 0xff);
+ d = d >> 8;
+ crc = CRC8 (crc, d & 0xff);
+ return crc;
+}
+
+
+static unsigned long
+crc_eliding_c_comments (const char *buf, unsigned long crc)
+{
+ const char *p;
+ enum { cOTHER, /* */
+ cSTRING, /* "... */
+ cSBACKSLASH, /* "...\ */
+ cCHAR, /* '... */
+ cCBACKSLASH, /* '...\ */
+ cSLASH, /* / */
+ cSLASH_SLASH, /* //... */
+ cSLASH_STAR, /* / *... */
+ cSTAR /* / *...* */
+ } ss = cOTHER;
+
+ for (p = buf; ;) {
+ unsigned char c = *p++;
+
+ switch (c) {
+ case 0:
+ switch (ss) {
+ case cOTHER:
+ return (crc);
+ case cSTRING: case cSBACKSLASH:
+ case cCHAR: case cCBACKSLASH:
+ case cSLASH: case cSLASH_SLASH: case cSLASH_STAR: case cSTAR:
+ fprintf (stderr, "Inopportune EOF: %s\n", buf);
+ exit (1);
+ }
+ break;
+ case '\"':
+ switch (ss) {
+ case cOTHER: ss = cSTRING; break; /* start string */
+ case cSTRING: ss = cOTHER; break; /* end string */
+ case cSBACKSLASH: ss = cSTRING; break;
+ case cCHAR: break;
+ case cCBACKSLASH: ss = cCHAR; break;
+ case cSLASH: crc = CRC8 (crc, '/'); ss = cOTHER; break;
+ case cSLASH_SLASH: continue; /* in comment */
+ case cSLASH_STAR: continue; /* in comment */
+ case cSTAR: ss = cSLASH_STAR; continue; /* in comment */
+ }
+ break;
+ case '\\':
+ switch (ss) {
+ case cOTHER: break;
+ case cSTRING: ss = cSBACKSLASH; break;
+ case cSBACKSLASH: ss = cSTRING; break;
+ case cCHAR: ss = cCBACKSLASH; break;
+ case cCBACKSLASH: ss = cCHAR; break;
+ case cSLASH: crc = CRC8 (crc, '/'); ; ss = cOTHER; break;
+ case cSLASH_SLASH: continue; /* in comment */
+ case cSLASH_STAR: continue; /* in comment */
+ case cSTAR: ss = cSLASH_STAR; continue; /* in comment */
+ }
+ break;
+ case '/':
+ switch (ss) {
+ case cOTHER: ss = cSLASH; continue; /* potential comment */
+ case cSTRING: break;
+ case cSBACKSLASH: ss = cSTRING; break;
+ case cCHAR: break;
+ case cCBACKSLASH: ss = cCHAR; break;
+ case cSLASH: ss = cSLASH_SLASH; continue; /* start comment */
+ case cSLASH_SLASH: continue; /* in comment */
+ case cSLASH_STAR: continue; /* in comment */
+ case cSTAR: ss = cOTHER; continue; /* end of comment */
+ }
+ break;
+ case '*':
+ switch (ss) {
+ case cOTHER: break;
+ case cSTRING: break;
+ case cSBACKSLASH: ss = cSTRING; break;
+ case cCHAR: break;
+ case cCBACKSLASH: ss = cCHAR; break;
+ case cSLASH: ss = cSLASH_STAR; continue; /* start comment */
+ case cSLASH_SLASH: continue; /* in comment */
+ case cSLASH_STAR: ss = cSTAR; continue; /* potential end */
+ case cSTAR: continue; /* still potential end of comment */
+ }
+ break;
+ case '\n': case '\r': case ' ': case '\t': case '\014':
+ switch (ss) {
+ case cOTHER: continue; /* ignore all whitespace */
+ case cSTRING: break;
+ case cSBACKSLASH: ss = cSTRING; break;
+ case cCHAR: break;
+ case cCBACKSLASH: ss = cCHAR; break;
+ case cSLASH: c = '/'; ss = cOTHER; break;
+ case cSLASH_SLASH:
+ if (c == '\n' || c == '\r') ss = cOTHER; /* end comment */
+ continue;
+ case cSLASH_STAR: continue; /* in comment */
+ case cSTAR: ss = cSLASH_STAR; continue; /* in comment */
+ }
+ default:
+ switch (ss) {
+ case cOTHER: break;
+ case cSTRING: break;
+ case cSBACKSLASH: ss = cSTRING; break;
+ case cCHAR: break;
+ case cCBACKSLASH: ss = cCHAR; break;
+ case cSLASH: crc = CRC8 (crc, '/'); ss = cOTHER; break;
+ case cSLASH_SLASH: continue; /* in comment */
+ case cSLASH_STAR: continue; /* in comment */
+ case cSTAR: ss = cSLASH_STAR; continue; /* in comment */
+ }
+ }
+ crc = CRC8 (crc, c);
+ }
+}
+
+/*
+ * main
+ */
+int main (int argc, char **argv)
+{
+ int curarg = 1;
+ char *ofile=0;
+ char *pythonfile=0;
+ char *jsonfile=0;
+ char *show_name=0;
+
+ while (curarg < argc) {
+ if (!strncmp (argv [curarg], "--verbose", 3)) {
+ fprintf (stderr, "%s version %s\n", argv [0], version);
+ curarg++;
+ continue;
+ }
+
+ if (!strncmp (argv [curarg], "--yydebug", 3)) {
+ yydebug = 1;
+ curarg++;
+ continue;
+ }
+
+ if (!strncmp (argv [curarg], "--dump", 3)) {
+ dump_tree = 1;
+ curarg++;
+ continue;
+ }
+
+ if (!strncmp (argv[curarg], "--show-name", 3)) {
+ curarg++;
+ if (curarg < argc) {
+ show_name = argv[curarg];
+ curarg++;
+ continue;
+ } else {
+ fprintf(stderr, "Missing filename after --show-name \n");
+ exit(1);
+ }
+ }
+
+ if (!strncmp (argv [curarg], "--input", 3)) {
+ curarg++;
+ if (curarg < argc) {
+ input_filename = argv[curarg];
+ if (!strcmp (argv [curarg], "-"))
+ ifp = stdin;
+ else
+ ifp = fopen (argv [curarg], "r");
+ if (ifp == NULL) {
+ fprintf (stderr, "Couldn't open input file %s\n",
+ argv[curarg]);
+ exit (1);
+ }
+ curarg++;
+ } else {
+ fprintf(stderr, "Missing filename after --input\n");
+ exit(1);
+ }
+ continue;
+ }
+ if (!strncmp (argv [curarg], "--output", 3)) {
+ curarg++;
+ if (curarg < argc) {
+ ofp = fopen (argv[curarg], "w");
+ if (ofp == NULL) {
+ fprintf (stderr, "Couldn't open output file %s\n",
+ argv[curarg]);
+ exit (1);
+ }
+ ofile = argv[curarg];
+ curarg++;
+ } else {
+ fprintf(stderr, "Missing filename after --output\n");
+ exit(1);
+ }
+ continue;
+ }
+ if (!strncmp (argv [curarg], "--python", 8)) {
+ curarg++;
+ if (curarg < argc) {
+ if (!strcmp(argv[curarg], "-")) {
+ pythonfp = stdout;
+ } else {
+ pythonfp = fopen(argv[curarg], "w");
+ pythonfile = argv[curarg];
+ }
+ if (pythonfp == NULL) {
+ fprintf (stderr, "Couldn't open python output file %s\n",
+ argv[curarg]);
+ exit (1);
+ }
+ curarg++;
+ } else {
+ fprintf(stderr, "Missing filename after --python\n");
+ exit(1);
+ }
+ continue;
+ }
+ if (!strncmp (argv [curarg], "--json", 6)) {
+ curarg++;
+ if (curarg < argc) {
+ if (!strcmp(argv[curarg], "-")) {
+ jsonfp = stdout;
+ } else {
+ jsonfp = fopen(argv[curarg], "w");
+ jsonfile = argv[curarg];
+ }
+ if (jsonfp == NULL) {
+ fprintf (stderr, "Couldn't open JSON output file %s\n",
+ argv[curarg]);
+ exit (1);
+ }
+ curarg++;
+ } else {
+ fprintf(stderr, "Missing filename after --json\n");
+ exit(1);
+ }
+ continue;
+ }
+ if (!strncmp (argv [curarg], "--app", 4)) {
+ curarg++;
+ if (curarg < argc) {
+ vlib_app_name = argv[curarg];
+ curarg++;
+ } else {
+ fprintf(stderr, "Missing app name after --app\n");
+ exit(1);
+ }
+ continue;
+ }
+
+ usage(argv[0]);
+ exit (1);
+ }
+ if (ofp == NULL) {
+ ofile = 0;
+ }
+ if (pythonfp == NULL) {
+ pythonfile = 0;
+ }
+ if (jsonfp == NULL) {
+ jsonfile = 0;
+ }
+ if (ifp == NULL) {
+ fprintf(stderr, "No input file specified...\n");
+ exit(1);
+ }
+ if (show_name) {
+ input_filename = show_name;
+ }
+
+ starttime = time (0);
+
+ if (yyparse() == 0) {
+ fclose (ifp);
+ curarg -= 2;
+ if (ofile) {
+ printf ("Output written to %s\n", ofile);
+ fclose (ofp);
+ }
+ if (pythonfile) {
+ printf ("Python bindings written to %s\n", pythonfile);
+ fclose (pythonfp);
+ }
+ if (jsonfile) {
+ printf ("JSON bindings written to %s\n", jsonfile);
+ fclose (jsonfp);
+ }
+ }
+ else {
+ fclose (ifp);
+ if (ofp)
+ fclose (ofp);
+ if (ofile) {
+ printf ("Removing %s\n", ofile);
+ unlink (ofile);
+ }
+ if (pythonfile) {
+ printf ("Removing %s\n", pythonfile);
+ unlink (pythonfile);
+ }
+ if (jsonfile) {
+ printf ("Removing %s\n", jsonfile);
+ unlink (jsonfile);
+ }
+ exit (1);
+ }
+ exit (0);
+}
+
+/*
+ * usage
+ */
+static void usage (char *progname)
+{
+ fprintf (stderr,
+ "usage: %s --input <filename> [--output <filename>] "
+ "[--json <filename>] [--python <filename>]\n%s",
+ progname,
+ " [--yydebug] [--dump-tree]\n");
+ exit (1);
+}
+
+/*
+ * yyerror
+ */
+void yyerror (char *s)
+{
+ fprintf (stderr, "%s:%d %s\n", current_filename, the_lexer_linenumber, s);
+}
+
+static char namebuf [MAXNAME];
+
+static inline char
+getc_char (FILE *ifp)
+{
+ char rv;
+
+ if (have_ungetc_char) {
+ have_ungetc_char = 0;
+ return saved_ungetc_char;
+ }
+
+ if (clib_fifo_elts (push_input_fifo)) {
+ clib_fifo_sub1(push_input_fifo, rv);
+ return (rv & 0x7f);
+ }
+ return ((char)(getc(ifp) & 0x7f));
+}
+
+u32 fe (char *fifo)
+{
+ return clib_fifo_elts (fifo);
+}
+
+static inline void
+ungetc_char (char c, FILE *ifp)
+{
+ saved_ungetc_char = c;
+ have_ungetc_char = 1;
+}
+
+void autoreply (void *np_arg)
+{
+ static u8 *s;
+ node_t *np = (node_t *)np_arg;
+ int i;
+
+ vec_reset_length (s);
+
+ s = format (0, " define %s_reply\n", (char *)(np->data[0]));
+ s = format (s, "{\n");
+ s = format (s, " u32 context;\n");
+ s = format (s, " i32 retval;\n");
+ s = format (s, "};\n");
+
+ for (i = 0; i < vec_len (s); i++)
+ clib_fifo_add1 (push_input_fifo, s[i]);
+}
+
+/*
+ * yylex (well, yylex_1: The real yylex below does crc-hackery)
+ */
+static int yylex_1 (void)
+{
+ int nameidx=0;
+ char c;
+ enum { LP_INITIAL_WHITESPACE, LP_LINE_NUMBER,
+ LP_PRE_FILENAME_WHITESPACE, LP_FILENAME,
+ LP_POST_FILENAME,
+ LP_OTHER
+ } lp_substate = LP_INITIAL_WHITESPACE;
+
+ again:
+ switch (the_lexer_state) {
+ /*
+ * START state -- looking for something interesting
+ */
+ case START_STATE:
+ c = getc_char (ifp);
+ if (feof (ifp))
+ return (EOF);
+
+ switch (c) {
+ case '\n':
+ the_lexer_linenumber++;
+ goto again;
+
+ case '#':
+ the_lexer_state = LINE_PRAGMA_STATE;
+ lp_substate = LP_INITIAL_WHITESPACE;
+ goto again;
+
+ /* FALLTHROUGH */
+ case '\t':
+ case ' ':
+ goto again;
+
+ case '(':
+ return (LPAR);
+
+ case ')':
+ return (RPAR);
+
+ case ';':
+ return (SEMI);
+
+ case '[':
+ return (LBRACK);
+
+ case ']':
+ return (RBRACK);
+
+ case '{':
+ return (LCURLY);
+
+ case '}':
+ return (RCURLY);
+
+ case ',':
+ return (COMMA);
+
+ case '"':
+ nameidx = 0;
+ the_lexer_state = STRING_STATE;
+ goto again;
+
+ case '@':
+ nameidx = 0;
+ the_lexer_state = HELPER_STATE;
+ goto again;
+
+ case '/':
+ c = getc_char (ifp);
+ if (feof (ifp))
+ return (EOF);
+
+ if (c == '/') {
+ the_lexer_state = CPP_COMMENT_STATE;
+ goto again;
+ } else if (c == '*') {
+ the_lexer_state = C_COMMENT_STATE;
+ goto again;
+ } else {
+ fprintf (stderr, "unknown token /%c at line %d\n",
+ c, the_lexer_linenumber);
+ return (BARF);
+ }
+
+ case '\\':
+ c = getc_char (ifp);
+ if (feof (ifp))
+ return (EOF);
+
+ /* Note fallthrough... */
+
+ default:
+ if (isalpha (c) || c == '_') {
+ namebuf [0] = c;
+ nameidx = 1;
+ the_lexer_state = NAME_STATE;
+ goto again;
+ } else if (isdigit(c)) {
+ namebuf [0] = c;
+ nameidx = 1;
+ the_lexer_state = NUMBER_STATE;
+ goto again;
+ }
+
+ fprintf (stderr, "unknown token %c at line %d\n",
+ c, the_lexer_linenumber);
+ return (BARF);
+ }
+
+ /*
+ * NAME state -- eat the rest of a name
+ */
+ case NAME_STATE:
+ c = getc_char (ifp);
+ if (feof (ifp))
+ return (EOF);
+
+ if (!isalnum (c) && c != '_') {
+ ungetc_char (c, ifp);
+ namebuf [nameidx] = 0;
+ the_lexer_state = START_STATE;
+ return (name_check (namebuf, &yylval));
+ }
+ if (nameidx >= (MAXNAME-1)) {
+ fprintf(stderr, "lex input buffer overflow...\n");
+ exit(1);
+ }
+ namebuf [nameidx++] = c;
+ goto again;
+
+ /*
+ * NUMBER state -- eat the rest of a number
+ */
+ case NUMBER_STATE:
+ c = getc_char (ifp);
+ if (feof (ifp))
+ return (EOF);
+
+ if (!isdigit (c)) {
+ ungetc_char (c, ifp);
+ namebuf [nameidx] = 0;
+ the_lexer_state = START_STATE;
+ yylval = (void *) atol(namebuf);
+ return (NUMBER);
+ }
+ if (nameidx >= (MAXNAME-1)) {
+ fprintf(stderr, "lex input buffer overflow...\n");
+ exit(1);
+ }
+ namebuf [nameidx++] = c;
+ goto again;
+
+ /*
+ * C_COMMENT state -- eat a peach
+ */
+ case C_COMMENT_STATE:
+ c = getc_char (ifp);
+ if (feof (ifp))
+ return (EOF);
+ if (c == '*') {
+ c = getc_char (ifp);
+ if (feof (ifp))
+ return (EOF);
+ if (c == '/') {
+ the_lexer_state = START_STATE;
+ goto again;
+ }
+ }
+ if (c == '\n')
+ the_lexer_linenumber++;
+ goto again;
+
+ /*
+ * CPP_COMMENT state -- eat a plum
+ */
+
+ case CPP_COMMENT_STATE:
+ c = getc_char (ifp);
+ if (feof (ifp))
+ return (EOF);
+ if (c == '\n') {
+ the_lexer_linenumber++;
+ the_lexer_state = START_STATE;
+ goto again;
+ }
+ goto again;
+
+ case STRING_STATE:
+ c = getc_char (ifp);
+ if (feof (ifp))
+ return (EOF);
+ switch (c) {
+ case '\\':
+ c = getc_char (ifp);
+ if (feof (ifp))
+ return (EOF);
+ namebuf[nameidx++] = c;
+ goto again;
+
+ case '"':
+ namebuf[nameidx] = 0;
+ yylval = (YYSTYPE) sxerox (namebuf);
+ the_lexer_state = START_STATE;
+ return (STRING);
+
+ default:
+ if (c == '\n')
+ the_lexer_linenumber++;
+
+ if (nameidx >= (MAXNAME-1)) {
+ fprintf(stderr, "lex input buffer overflow...\n");
+ exit(1);
+ }
+ namebuf[nameidx++] = c;
+ goto again;
+ }
+ break;
+
+ case HELPER_STATE:
+ c = getc_char (ifp);
+ if (feof (ifp))
+ return (EOF);
+ switch (c) {
+ case '\\':
+ c = getc_char (ifp);
+ if (feof (ifp))
+ return (EOF);
+ namebuf[nameidx] = c;
+ goto again;
+
+ case '@':
+ namebuf[nameidx] = 0;
+ yylval = (YYSTYPE) sxerox (namebuf);
+ the_lexer_state = START_STATE;
+ return (HELPER_STRING);
+
+ default:
+ if (c == '\n')
+ the_lexer_linenumber++;
+
+ /*
+ * CPP makes it approximately impossible to
+ * type "#define FOO 123", so we provide a
+ * lexical trick to achieve that result
+ */
+
+ if (c == '$')
+ c = '#';
+
+ if (nameidx >= (MAXNAME-1)) {
+ fprintf(stderr, "lex input buffer overflow...\n");
+ exit(1);
+ }
+ namebuf[nameidx++] = c;
+ goto again;
+ }
+ break;
+
+ case LINE_PRAGMA_STATE:
+ /* We're only interested in lines of the form # 259 "foo.c" 17 */
+
+ switch (lp_substate) {
+
+ case LP_INITIAL_WHITESPACE: /* no number seen yet */
+ c = getc_char(ifp);
+ if (feof(ifp))
+ return(EOF);
+ if (c >= '0' && c <= '9') {
+ namebuf[nameidx++] = c;
+ lp_substate = LP_LINE_NUMBER;
+ } else if (c == '\n') {
+ goto lp_end_of_line;
+ } else if (c != ' ' && c != '\t') {
+ /* Nothing */
+ } else {
+ lp_substate = LP_OTHER;
+ }
+ goto again;
+
+ case LP_LINE_NUMBER: /* eating linenumber */
+ c = getc_char(ifp);
+ if (feof(ifp))
+ return(EOF);
+ if (c >= '0' && c <= '9') {
+ namebuf[nameidx++] = c;
+ } else if (c == ' ' || c == '\t') {
+ namebuf[nameidx++] = 0;
+ the_lexer_linenumber = atol(namebuf);
+ lp_substate = LP_PRE_FILENAME_WHITESPACE;
+ } else if (c == '\n') {
+ goto lp_end_of_line;
+ } else {
+ lp_substate = LP_OTHER;
+ }
+ goto again;
+
+ case LP_PRE_FILENAME_WHITESPACE: /* awaiting filename */
+ c = getc_char(ifp);
+ if (feof(ifp))
+ return(EOF);
+
+ if (c == '"') {
+ lp_substate = LP_FILENAME;
+ nameidx = 0;
+ } else if (c == ' ' || c == '\t') {
+ /* nothing */
+ } else if (c == '\n') {
+ goto lp_end_of_line;
+ } else {
+ lp_substate = LP_OTHER;
+ }
+ goto again;
+
+ case LP_FILENAME: /* eating filename */
+ c = getc_char(ifp);
+ if (feof(ifp))
+ return(EOF);
+
+ if (c == '"') {
+ lp_substate = LP_POST_FILENAME;
+ namebuf[nameidx] = 0;
+ } else if (c == '\n') {
+ goto lp_end_of_line; /* syntax error... */
+ } else {
+ namebuf[nameidx++] = c;
+ }
+ goto again;
+
+ case LP_POST_FILENAME: /* ignoring rest of line */
+ case LP_OTHER:
+ c = getc_char(ifp);
+ if (feof(ifp))
+ return(EOF);
+
+ if (c == '\n') {
+ if (lp_substate == LP_POST_FILENAME) {
+ if (current_filename_allocated) {
+ current_filename_allocated = 0;
+ free(current_filename);
+ }
+
+ if (!strcmp(namebuf, "<stdin>")) {
+ current_filename = input_filename;
+ } else {
+ current_filename = sxerox(namebuf);
+ current_filename_allocated = 1;
+ }
+ }
+ lp_end_of_line:
+ the_lexer_state = START_STATE;
+ nameidx = 0;
+ }
+ goto again;
+ }
+ break;
+ }
+ fprintf (stderr, "LEXER BUG!\n");
+ exit (1);
+ /* NOTREACHED */
+ return (0);
+}
+
+/*
+ * Parse a token and side-effect input_crc
+ * in a whitespace- and comment-insensitive fashion.
+ */
+int yylex (void)
+{
+ /*
+ * Accumulate a crc32-based signature while processing the
+ * input file. The goal is to come up with a magic number
+ * which changes precisely when the original input file changes
+ * but which ignores whitespace changes.
+ */
+ unsigned long crc = input_crc;
+ int node_type = yylex_1 ();
+ unsigned long crc2 = message_crc;
+ int use_helper_string = 0;
+ unsigned short code;
+
+ switch (node_type) {
+ case PRIMTYPE:
+ case NAME:
+ case NUMBER:
+ case STRING:
+ case HELPER_STRING:
+ use_helper_string = 1;
+ break;
+
+ /* Other node types have no "substate" */
+ /* This code is written in this curious fashion because we
+ * want the generated CRC to be independent of the particular
+ * values a particular version of lex/bison assigned to various states.
+ */
+
+ case RPAR: code = 258; break;
+ case LPAR: code = 259; break;
+ case SEMI: code = 260; break;
+ case LBRACK: code = 261; break;
+ case RBRACK: code = 262; break;
+ case BARF: code = 265; break;
+ case TPACKED: code = 266; break;
+ case DEFINE: code = 267; break;
+ case LCURLY: code = 268; break;
+ case RCURLY: code = 269; break;
+ case UNION: code = 271; break;
+ case COMMA: code = 273; break;
+ case NOVERSION: code = 274; break;
+ case MANUAL_PRINT: code = 275; break;
+ case MANUAL_ENDIAN: code = 276; break;
+ case TYPEONLY: code = 278; break;
+ case DONT_TRACE: code = 279; break;
+ case AUTOREPLY: code = 280; break;
+
+ case EOF: code = ~0; break; /* hysterical compatibility */
+
+ default:
+ fprintf(stderr, "yylex: node_type %d missing state CRC cookie\n",
+ node_type);
+ exit(1);
+ }
+
+ if (use_helper_string)
+ {
+ /* We know these types accumulated token text into namebuf */
+ /* HELPER_STRING may still contain C comments. Argh. */
+ crc = crc_eliding_c_comments (namebuf, crc);
+ crc2 = crc_eliding_c_comments (namebuf, crc2);
+ } else
+ {
+ crc = CRC16 (crc, code);
+ crc2 = CRC16 (crc2, code);
+ }
+
+ input_crc = crc;
+ message_crc = crc2;
+ return (node_type);
+}
+
+/*
+ * name_check -- see if the name we just ate
+ * matches a known keyword. If so, set yylval
+ * to a new instance of <subclass of node>, and return PARSER_MACRO
+ *
+ * Otherwise, set yylval to sxerox (s) and return NAME
+ */
+
+static struct keytab {
+ char *name;
+ enum node_subclass subclass_id;
+} keytab [] =
+/* Keep the table sorted, binary search used below! */
+{
+ {"autoreply", NODE_AUTOREPLY},
+ {"define", NODE_DEFINE},
+ {"dont_trace", NODE_DONT_TRACE},
+ {"f64", NODE_F64},
+ {"i16", NODE_I16},
+ {"i32", NODE_I32},
+ {"i64", NODE_I64},
+ {"i8", NODE_I8},
+ {"manual_endian", NODE_MANUAL_ENDIAN},
+ {"manual_print", NODE_MANUAL_PRINT},
+ {"noversion", NODE_NOVERSION},
+ {"packed", NODE_PACKED},
+ {"typeonly", NODE_TYPEONLY},
+ {"u16", NODE_U16},
+ {"u32", NODE_U32},
+ {"u64", NODE_U64},
+ {"u8", NODE_U8},
+ {"union", NODE_UNION},
+ {"uword", NODE_UWORD},
+};
+
+static int name_check (const char *s, YYSTYPE *token_value)
+{
+ enum node_subclass subclass_id;
+ int top, bot, mid;
+ int result;
+
+ for (top = 0, bot = (sizeof(keytab) / sizeof(struct keytab))-1;
+ bot >= top; ) {
+ mid = (top + bot) / 2;
+ result = name_compare (s, keytab[mid].name);
+ if (result < 0)
+ bot = mid - 1;
+ else if (result > 0)
+ top = mid + 1;
+ else {
+ subclass_id = keytab[mid].subclass_id;
+
+ switch (subclass_id) {
+ case NODE_U8:
+ case NODE_U16:
+ case NODE_U32:
+ case NODE_U64:
+ case NODE_I8:
+ case NODE_I16:
+ case NODE_I32:
+ case NODE_I64:
+ case NODE_F64:
+ case NODE_UWORD:
+ *token_value = make_node(subclass_id);
+ return (PRIMTYPE);
+
+ case NODE_PACKED:
+ *token_value = make_node(subclass_id);
+ return (TPACKED);
+
+ case NODE_DEFINE:
+ message_crc = 0;
+ *token_value = make_node(subclass_id);
+ return(DEFINE);
+
+ case NODE_MANUAL_PRINT:
+ *token_value = (YYSTYPE) NODE_FLAG_MANUAL_PRINT;
+ return (MANUAL_PRINT);
+
+ case NODE_MANUAL_ENDIAN:
+ *token_value = (YYSTYPE) NODE_FLAG_MANUAL_ENDIAN;
+ return (MANUAL_ENDIAN);
+
+ case NODE_TYPEONLY:
+ *token_value = (YYSTYPE) NODE_FLAG_TYPEONLY;
+ return(TYPEONLY);
+
+ case NODE_DONT_TRACE:
+ *token_value = (YYSTYPE) NODE_FLAG_DONT_TRACE;
+ return(DONT_TRACE);
+
+ case NODE_AUTOREPLY:
+ *token_value = (YYSTYPE) NODE_FLAG_AUTOREPLY;
+ return(AUTOREPLY);
+
+ case NODE_NOVERSION:
+ return(NOVERSION);
+
+ case NODE_UNION:
+ return(UNION);
+
+ default:
+ fprintf (stderr, "fatal: keytab botch!\n");
+ exit (1);
+ }
+ }
+ }
+ *token_value = (YYSTYPE) sxerox (s);
+ return (NAME);
+}
+
+/*
+ * sxerox
+ */
+
+char *sxerox (const char *s)
+{
+ int len = strlen (s);
+ char *rv;
+
+ rv = (char *) malloc (len+1);
+ if (rv == 0) {
+ fprintf(stderr, "Out of memory...");
+ exit (1);
+ }
+
+ strcpy (rv, s);
+ return (rv);
+}
+
+/*
+ * name_compare
+ */
+
+int name_compare (const char *s1, const char *s2)
+{
+ char c1, c2;
+
+ while (*s1 && *s2) {
+ c1 = *s1++;
+ c2 = *s2++;
+
+ c1 = tolower (c1);
+ c2 = tolower (c2);
+ if (c1 < c2)
+ return (-1);
+ else if (c1 > c2)
+ return (1);
+ }
+ if (*s1 < *s2)
+ return (-1);
+ else if (*s1 > *s2)
+ return (1);
+ return (0);
+}
diff --git a/src/tools/vppapigen/lex.h b/src/tools/vppapigen/lex.h
new file mode 100644
index 00000000..275cf685
--- /dev/null
+++ b/src/tools/vppapigen/lex.h
@@ -0,0 +1,51 @@
+/*
+ *------------------------------------------------------------------
+ * lex.h - definitions for the api generator's lexical
+ * analyzer.
+ *
+ * Copyright (c) 1996-2009 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 _LEX_H_
+#define _LEX_H_ 1
+
+extern int yylex (void);
+extern void yyerror (char *);
+extern int yyparse (void);
+extern void autoreply (void *);
+
+#ifndef YYSTYPE
+#define YYSTYPE void *
+#endif
+
+#include "tools/vppapigen/gram.h"
+
+enum lex_state {
+ START_STATE = 1,
+ NAME_STATE,
+ NUMBER_STATE,
+ C_COMMENT_STATE,
+ CPP_COMMENT_STATE,
+ STRING_STATE,
+ HELPER_STATE,
+ LINE_PRAGMA_STATE,
+};
+
+#define MAXNAME 64000
+
+extern unsigned long input_crc;
+extern unsigned long message_crc;
+
+#endif /* _LEX_H_ */
diff --git a/src/tools/vppapigen/node.c b/src/tools/vppapigen/node.c
new file mode 100644
index 00000000..1f9905ba
--- /dev/null
+++ b/src/tools/vppapigen/node.c
@@ -0,0 +1,1547 @@
+/*
+ *------------------------------------------------------------------
+ * node.c - the api generator's semantic back-end
+ *
+ * Copyright (c) 2004-2009 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 <stdbool.h>
+#include <ctype.h>
+#include <time.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <vppinfra/vec.h>
+#include <vppinfra/hash.h>
+
+#include "lex.h"
+#include "node.h"
+
+#define YYSTYPE void *
+
+FILE *ofp;
+FILE *pythonfp;
+FILE *jsonfp;
+time_t starttime;
+char *vlib_app_name;
+char *input_filename;
+node_vft_t *the_vft[NODE_N_TYPES];
+static int indent;
+static int dont_output_version;
+int dump_tree;
+static char *fixed_name;
+static char tmpbuf [MAXNAME];
+static char *current_def_name;
+static char *current_union_name;
+static char *current_type_fmt;
+static char *current_type_cast;
+static char current_id;
+static char current_is_complex;
+static char *current_endianfun;
+static char *current_type_name;
+
+void indent_me(FILE *ofp)
+{
+ int i;
+
+ for (i = 0; i < indent; i++)
+ putc(' ', ofp);
+}
+
+char *uppercase (char *s)
+{
+ char *cp;
+
+ cp = tmpbuf;
+
+ while (*s && (cp < tmpbuf + (sizeof(tmpbuf)-1))) {
+ if (*s >= 'a' && *s <= 'z')
+ *cp++ = *s++ - ('a' - 'A');
+ else
+ *cp++ = *s++;
+ }
+ *cp = 0;
+ return(tmpbuf);
+}
+
+char *lowercase (char *s)
+{
+ char *cp;
+
+ cp = tmpbuf;
+
+ while (*s && (cp < tmpbuf + (sizeof(tmpbuf)-1))) {
+ if (*s >= 'A' && *s <= 'Z')
+ *cp++ = *s++ + ('a' - 'A');
+ else
+ *cp++ = *s++;
+ }
+ *cp = 0;
+ return(tmpbuf);
+}
+
+void primtype_recursive_print(node_t *this, i8 *fmt)
+{
+ fputs((char *)fmt, stdout);
+
+ if (this->deeper) {
+ node_vft_t *vftp = the_vft[this->deeper->type];
+ vftp->print(this->deeper);
+ }
+}
+
+void primtype_recursive_generate(node_t *this, enum passid which, FILE *ofp,
+ i8 *type_name, i8 *type_fmt, i8 *type_cast)
+{
+ node_vft_t *vftp;
+
+ current_type_name = (char *)type_name;
+ current_type_cast = (char *)type_cast;
+
+ switch(which) {
+ case TYPEDEF_PASS:
+ fputs((char *)type_name, ofp);
+ fputs(" ", ofp);
+ break;
+
+ case PRINTFUN_PASS:
+ current_type_fmt = (char *)type_fmt;
+ break;
+
+ case ENDIANFUN_PASS:
+ vftp = the_vft[this->type];
+ current_endianfun = vftp->endian_converter;
+ break;
+
+ case PYTHON_PASS:
+ fputs("('", pythonfp);
+ fputs((char *)type_name, pythonfp);
+ fputs("', ", pythonfp);
+ break;
+
+ case JSON_PASS:
+ fputs("[\"", jsonfp);
+ fputs((char *)type_name, jsonfp);
+ fputs("\", ", jsonfp);
+ break;
+
+ default:
+ fprintf(stderr, "primtype_recursive_generate: unimp pass %d\n", which);
+ break;
+ }
+
+ if (this->deeper) {
+ vftp = the_vft[this->deeper->type];
+ vftp->generate(this->deeper, which, ofp);
+ }
+}
+
+void node_illegal_print (node_t *this)
+{
+ fprintf(stderr, "node_illegal_print called\n");
+ exit(0);
+}
+
+void node_illegal_generate (node_t *this, enum passid notused, FILE *ofp)
+{
+ fprintf(stderr, "node_illegal_generate called\n");
+ exit(0);
+}
+
+node_vft_t node_illegal_vft = {
+ node_illegal_print,
+ node_illegal_generate,
+ "illegal"
+};
+
+void node_u8_print (node_t *this)
+{
+ primtype_recursive_print(this, "u8 ");
+}
+
+void node_u8_generate (node_t *this, enum passid which, FILE *ofp)
+{
+ primtype_recursive_generate(this, which, ofp, "u8", "%u", "(unsigned)");
+}
+
+node_vft_t node_u8_vft = {
+ node_u8_print,
+ node_u8_generate,
+ NULL
+};
+
+void node_u16_print (node_t *this)
+{
+ primtype_recursive_print(this, "u16 ");
+}
+
+void node_u16_generate (node_t *this, enum passid which, FILE *ofp)
+{
+ primtype_recursive_generate(this, which, ofp, "u16", "%u", "(unsigned)");
+}
+
+node_vft_t node_u16_vft = {
+ node_u16_print,
+ node_u16_generate,
+ "clib_net_to_host_u16"
+};
+
+void node_u32_print (node_t *this)
+{
+ primtype_recursive_print(this, "u32 ");
+}
+
+void node_u32_generate (node_t *this, enum passid which, FILE *ofp)
+{
+ primtype_recursive_generate(this, which, ofp, "u32", "%u", "(unsigned)");
+}
+
+node_vft_t node_u32_vft = {
+ node_u32_print,
+ node_u32_generate,
+ "clib_net_to_host_u32",
+};
+
+void node_u64_print (node_t *this)
+{
+ primtype_recursive_print(this, "u64 ");
+}
+
+void node_u64_generate (node_t *this, enum passid which, FILE *ofp)
+{
+ primtype_recursive_generate(this, which, ofp, "u64", "%llu",
+ "(long long)");
+}
+
+node_vft_t node_u64_vft = {
+ node_u64_print,
+ node_u64_generate,
+ "clib_net_to_host_u64"
+};
+
+void node_i8_print (node_t *this)
+{
+ primtype_recursive_print(this, "i8 ");
+}
+
+void node_i8_generate (node_t *this, enum passid which, FILE *ofp)
+{
+ primtype_recursive_generate(this, which, ofp, "i8", "%d", "(int)");
+}
+
+node_vft_t node_i8_vft = {
+ node_i8_print,
+ node_i8_generate,
+ ""
+};
+
+void node_i16_print (node_t *this)
+{
+ primtype_recursive_print(this, "i16 ");
+}
+
+void node_i16_generate (node_t *this, enum passid which, FILE *ofp)
+{
+ primtype_recursive_generate(this, which, ofp, "i16", "%d", "(int)");
+}
+
+node_vft_t node_i16_vft = {
+ node_i16_print,
+ node_i16_generate,
+ "clib_net_to_host_u16"
+};
+
+void node_i32_print (node_t *this)
+{
+ primtype_recursive_print(this, "i32 ");
+}
+
+void node_i32_generate (node_t *this, enum passid which, FILE *ofp)
+{
+ primtype_recursive_generate(this, which, ofp, "i32", "%ld", "(long)");
+}
+
+node_vft_t node_i32_vft = {
+ node_i32_print,
+ node_i32_generate,
+ "clib_net_to_host_u32"
+};
+
+void node_i64_print (node_t *this)
+{
+ primtype_recursive_print(this, "i64 ");
+}
+
+void node_i64_generate (node_t *this, enum passid which, FILE *ofp)
+{
+ primtype_recursive_generate(this, which, ofp, "i64", "%lld",
+ "(long long)");
+}
+
+node_vft_t node_i64_vft = {
+ node_i64_print,
+ node_i64_generate,
+ "clib_net_to_host_u64"
+};
+
+void node_f64_print (node_t *this)
+{
+ primtype_recursive_print(this, "f64 ");
+}
+
+void node_f64_generate (node_t *this, enum passid which, FILE *ofp)
+{
+ primtype_recursive_generate(this, which, ofp, "f64", "%.2f",
+ "(double)");
+}
+
+node_vft_t node_f64_vft = {
+ node_f64_print,
+ node_f64_generate,
+ " ", /* FP numbers are sent in host byte order */
+};
+
+
+void node_packed_print (node_t *this)
+{
+ primtype_recursive_print (this, "packed ");
+}
+
+void node_packed_generate (node_t *this, enum passid which, FILE *ofp)
+{
+ primtype_recursive_generate(this, which, ofp, "PACKED", "", "");
+}
+
+node_vft_t node_packed_vft = {
+ node_packed_print,
+ node_packed_generate,
+ 0,
+};
+
+void node_define_print (node_t *this)
+{
+ fprintf(stdout, "define %s {\n", CDATA0);
+ if (this->deeper) {
+ node_vft_t *vftp = the_vft[this->deeper->type];
+ fprintf(stdout, " ");
+ vftp->print(this->deeper);
+ }
+ fprintf(stdout, "};\n");
+}
+
+void node_define_generate (node_t *this, enum passid which, FILE *fp)
+{
+ node_t *child;
+
+ switch(which) {
+ case TYPEDEF_PASS:
+ fprintf(fp, "typedef VL_API_PACKED(struct _vl_api_%s {\n", CDATA0);
+ child = this->deeper;
+ indent += 4;
+ while (child) {
+ node_vft_t *vftp = the_vft[child->type];
+ indent_me(fp);
+ vftp->generate(child, which, fp);
+ child = child->peer;
+ }
+ indent -= 4;
+ fprintf(fp, "}) vl_api_%s_t;\n\n", CDATA0);
+ break;
+
+ case ENDIANFUN_PASS:
+ case PRINTFUN_PASS:
+ child = this->deeper;
+ while (child) {
+ node_vft_t *vftp = the_vft[child->type];
+ vftp->generate(child, which, fp);
+ child = child->peer;
+ }
+ break;
+
+ case PYTHON_PASS:
+ fprintf(fp, "('%s',\n", CDATA0);
+ child = this->deeper;
+ indent += 4;
+ while (child) {
+ node_vft_t *vftp = the_vft[child->type];
+ indent_me(fp);
+ vftp->generate(child, which, fp);
+ child = child->peer;
+ }
+ indent -= 4;
+ fprintf(fp, "),\n\n");
+ break;
+
+ case JSON_PASS:
+ fprintf(fp, "[\"%s\",\n", CDATA0);
+ child = this->deeper;
+ indent += 4;
+ while (child) {
+ node_vft_t *vftp = the_vft[child->type];
+ indent_me(fp);
+ vftp->generate(child, which, fp);
+ child = child->peer;
+ fprintf(fp, ",\n");
+ }
+ indent_me(fp);
+ fprintf (fp, "{\"crc\" : \"0x%08x\"}\n", (u32)(uword)CDATA3);
+ indent -= 4;
+ indent_me(fp);
+ fprintf(fp, "]");
+ break;
+
+ default:
+ fprintf(stderr, "node_define_generate: unimp pass %d\n", which);
+ break;
+ }
+}
+
+node_vft_t node_define_vft = {
+ node_define_print,
+ node_define_generate,
+ 0,
+};
+
+void node_union_print (node_t *this)
+{
+ primtype_recursive_print (this, "union ");
+}
+
+void node_union_generate (node_t *this, enum passid which, FILE *fp)
+{
+ node_t *child;
+ node_t *uelem;
+ int case_id=1;
+
+ switch(which) {
+ case TYPEDEF_PASS:
+ fprintf(fp, "u8 _%s_which;\n", CDATA0);
+ indent_me(fp);
+ fprintf(fp, "union _%s {\n", CDATA0);
+ child = this->deeper;
+ indent += 4;
+
+ while (child) {
+ node_vft_t *vftp = the_vft[child->type];
+ indent_me(fp);
+ vftp->generate(child, which, fp);
+ child = child->peer;
+ }
+ indent -= 4;
+ indent_me(fp);
+ fprintf(fp, "} %s;\n", CDATA0);
+ break;
+
+ case PRINTFUN_PASS:
+ case ENDIANFUN_PASS:
+ uelem = this->deeper;
+
+ indent_me(fp);
+ fprintf(fp, "switch(a->_%s_which) {\n",
+ CDATA0);
+ indent += 4;
+ current_union_name = CDATA0;
+
+ /* Walk the list of objects in this union */
+ while (uelem) {
+ node_vft_t *vftp = the_vft[uelem->type];
+ indent -= 4;
+ indent_me(fp);
+ fprintf(fp, "case %d:\n", case_id);
+ case_id++;
+ indent += 4;
+ /* Drill down on each element */
+ vftp->generate(uelem, which, fp);
+ indent_me(fp);
+ fprintf(fp, "break;\n");
+ uelem = uelem->peer;
+ }
+ current_union_name = 0;
+ indent -= 4;
+ indent_me(fp);
+ fprintf(fp, "default:\n");
+ indent += 4;
+ indent_me(fp);
+ if (which == PRINTFUN_PASS) {
+ fprintf(fp,
+ "vl_print(handle, \"WARNING: _%s_which not set.\\n\");\n",
+ CDATA0);
+ }
+ indent_me(fp);
+ fprintf(fp, "break;\n");
+ indent -= 4;
+ indent_me(fp);
+ fprintf(fp, "}\n");
+ break;
+
+ default:
+ fprintf(stderr, "node_union_generate: unimp pass %d\n", which);
+ break;
+ }
+}
+
+
+node_vft_t node_union_vft = {
+ node_union_print,
+ node_union_generate,
+ 0,
+};
+
+void node_scalar_print (node_t *this)
+{
+ fprintf(stdout, "%s", CDATA0);
+ primtype_recursive_print (this, "");
+}
+
+void node_scalar_generate (node_t *this, enum passid which, FILE *fp)
+{
+ char *union_prefix = "";
+
+ if (current_union_name) {
+ sprintf(tmpbuf, "%s.", current_union_name);
+ union_prefix = tmpbuf;
+ }
+
+ switch(which) {
+ case TYPEDEF_PASS:
+ fprintf(fp, "%s;\n", CDATA0);
+ break;
+
+ case PRINTFUN_PASS:
+ indent_me(fp);
+ if (current_is_complex) {
+ fprintf(fp, "vl_api_%s_t_print(a->%s%s, handle);\n",
+ current_type_name, union_prefix, CDATA0);
+ } else {
+ if (!strcmp(current_type_fmt, "uword")) {
+ fprintf(fp,
+ "vl_print(handle, \"%s%s: \" _uword_fmt \"\\n\", %s a->%s%s);\n",
+ union_prefix, CDATA0, "(_uword_cast)",
+ union_prefix, CDATA0);
+ } else {
+ fprintf(fp,
+ "vl_print(handle, \"%s%s: %s\\n\", %s a->%s%s);\n",
+ union_prefix, CDATA0,
+ current_type_fmt, current_type_cast,
+ union_prefix, CDATA0);
+ }
+ }
+ break;
+
+ case ENDIANFUN_PASS:
+ indent_me(fp);
+ if (current_is_complex) {
+ fprintf(fp, "vl_api%s_t_endian(a->%s%s);\n",
+ current_type_name, union_prefix, CDATA0);
+ } else {
+ /* Current_endianfun == NULL means e.g. it's a u8... */
+ if (current_endianfun) {
+ fprintf(fp, "a->%s%s = %s(a->%s%s);\n", union_prefix,
+ CDATA0, current_endianfun,
+ union_prefix, CDATA0);
+ } else {
+ fprintf(fp, "/* a->%s%s = a->%s%s (no-op) */\n",
+ union_prefix, CDATA0,
+ union_prefix, CDATA0);
+ }
+ }
+ break;
+ case PYTHON_PASS:
+ fprintf(fp, "'%s'),\n", CDATA0);
+ break;
+
+ case JSON_PASS:
+ fprintf(fp, "\"%s\"]", CDATA0);
+ break;
+
+ default:
+ fprintf(stderr, "node_scalar_generate: unimp pass %d\n", which);
+ }
+ if (this->deeper) {
+ fprintf(stderr, "broken recursion in node_scalar_generate\n");
+ }
+}
+
+
+node_vft_t node_scalar_vft = {
+ node_scalar_print,
+ node_scalar_generate,
+ 0,
+};
+
+void node_vector_print (node_t *this)
+{
+ primtype_recursive_print (this, "vector ");
+}
+
+void node_vector_generate (node_t *this, enum passid which, FILE *fp)
+{
+ char *union_prefix = "";
+
+ if (current_union_name) {
+ sprintf(tmpbuf, "%s.", current_union_name);
+ union_prefix = tmpbuf;
+ }
+
+ switch(which) {
+ case TYPEDEF_PASS:
+ fprintf(fp, "%s[%d];\n", CDATA0, IDATA1);
+ break;
+
+ case PRINTFUN_PASS:
+ /* Don't bother about "u8 data [0];" et al. */
+ if (IDATA1 == 0)
+ break;
+
+ indent_me(fp);
+ fprintf(fp, "{\n");
+ indent += 4;
+ indent_me(fp);
+ fprintf(fp, "int _i;\n");
+ indent_me(fp);
+ fprintf(fp, "for (_i = 0; _i < %d; _i++) {\n",
+ IDATA1);
+ indent += 4;
+ indent_me(fp);
+ if (current_is_complex) {
+ fprintf(fp, "vl_print(handle, \"%s%s[%%d]: ",
+ union_prefix, CDATA0);
+ fprintf(fp,
+ "vl_print_%s (handle, a->%s%s[_i]);\n",
+ CDATA0, union_prefix, CDATA0);
+ } else {
+ fprintf(fp,
+ "vl_print(handle, \"%s%s[%%d]: %s\\n\", _i, a->%s%s[_i]);\n",
+ union_prefix, CDATA0,
+ current_type_fmt,
+ union_prefix, CDATA0);
+ }
+ indent -= 4;
+ indent_me(fp);
+ fprintf(fp, "}\n");
+ indent -= 4;
+ indent_me(fp);
+ fprintf(fp, "}\n");
+ break;
+
+ case ENDIANFUN_PASS:
+ /* Don't bother about "u8 data [0];" et al. */
+ if (IDATA1 == 0)
+ break;
+ /* If this is a simple endian swap, but the endian swap method is a no-op,
+ * then indicate this is a no-op in a comment.
+ */
+ if (!current_is_complex && current_endianfun == NULL) {
+ indent_me(fp);
+ fprintf(fp, "/* a->%s%s[0..%d] = a->%s%s[0..%d] (no-op) */\n",
+ union_prefix, CDATA0, IDATA1 - 1,
+ union_prefix, CDATA0, IDATA1 - 1);
+ break;
+ }
+
+ indent_me(fp);
+ fprintf(fp, "{\n");
+ indent += 4;
+ indent_me(fp);
+ fprintf(fp, "int _i;\n");
+ indent_me(fp);
+ fprintf(fp, "for (_i = 0; _i < %d; _i++) {\n",
+ IDATA1);
+ indent += 4;
+ indent_me(fp);
+ if (current_is_complex) {
+ fprintf(fp,
+ "vl_api_%s_t_endian (a->%s%s[_i]);\n",
+ current_type_name, union_prefix, CDATA0);
+ } else {
+ fprintf(fp,
+ "a->%s%s[_i] = %s(a->%s%s[_i]);\n",
+ union_prefix, CDATA0,
+ current_endianfun,
+ union_prefix, CDATA0);
+ }
+ indent -= 4;
+ indent_me(fp);
+ fprintf(fp, "}\n");
+ indent -= 4;
+ indent_me(fp);
+ fprintf(fp, "}\n");
+ break;
+ case PYTHON_PASS:
+ if (CDATA2 != 0) { // variable length vector
+ fprintf(fp, "'%s', '%d', '%s'),\n", CDATA0, IDATA1, CDATA2);
+ } else {
+ fprintf(fp, "'%s', '%d'),\n", CDATA0, IDATA1);
+ }
+ break;
+
+ case JSON_PASS:
+ if (CDATA2 != 0) { /* variable length vector */
+ fprintf(fp, "\"%s\", %d, \"%s\"]", CDATA0, IDATA1, CDATA2);
+ } else {
+ fprintf(fp, "\"%s\", %d]", CDATA0, IDATA1);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "node_vector_generate: unimp pass %d\n", which);
+ }
+ if (this->deeper) {
+ fprintf(stderr, "broken recursion in node_vector_generate\n");
+ }
+}
+
+node_vft_t node_vector_vft = {
+ node_vector_print,
+ node_vector_generate,
+ 0,
+};
+
+void node_complex_print (node_t *this)
+{
+ primtype_recursive_print (this, "complex ");
+}
+
+void node_complex_generate (node_t *this, enum passid which, FILE *fp)
+{
+ node_t *deeper;
+ node_vft_t *vftp;
+ char *member_name = "broken!";
+ char *union_prefix = "";
+
+ if (current_union_name) {
+ sprintf(tmpbuf, "%s.", current_union_name);
+ union_prefix = tmpbuf;
+ }
+
+ current_is_complex++;
+
+ switch(which) {
+ case TYPEDEF_PASS:
+ fprintf(fp, "%s ", CDATA0);
+ deeper = this->deeper;
+ if (deeper) {
+ vftp = the_vft[deeper->type];
+ vftp->generate(deeper, which, fp);
+ }
+ break;
+
+ case PRINTFUN_PASS:
+ deeper = this->deeper;
+ while (deeper) {
+ if (deeper->type == NODE_SCALAR ||
+ deeper->type == NODE_VECTOR) {
+ member_name = deeper->data[0];
+ break;
+ }
+ deeper = deeper->deeper;
+ }
+ indent_me(fp);
+ fprintf(fp, "vl_print(handle, \"%s%s ----- \\n\");\n",
+ union_prefix, member_name);
+ indent_me(fp);
+
+ if (deeper && deeper->type == NODE_VECTOR)
+ fprintf(fp, "%s_print(a->%s%s, handle);\n",
+ CDATA0, union_prefix, member_name);
+ else
+ fprintf(fp, "%s_print(&a->%s%s, handle);\n",
+ CDATA0, union_prefix, member_name);
+
+ indent_me(fp);
+ fprintf(fp, "vl_print(handle, \"%s%s ----- END \\n\");\n",
+ union_prefix, member_name);
+ break;
+
+ case ENDIANFUN_PASS:
+ deeper = this->deeper;
+ while (deeper) {
+ if (deeper->type == NODE_SCALAR ||
+ deeper->type == NODE_VECTOR) {
+ member_name = deeper->data[0];
+ break;
+ }
+ deeper = deeper->deeper;
+ }
+
+ indent_me(fp);
+ if (deeper && deeper->type == NODE_VECTOR)
+ fprintf(fp, "%s_endian(a->%s%s);\n",
+ CDATA0, union_prefix, member_name);
+ else
+ fprintf(fp, "%s_endian(&a->%s%s);\n",
+ CDATA0, union_prefix, member_name);
+ break;
+ case PYTHON_PASS:
+ fprintf(fp, "('%s',", CDATA0);
+ deeper = this->deeper;
+ if (deeper) {
+ vftp = the_vft[deeper->type];
+ vftp->generate(deeper, which, fp);
+ }
+ break;
+
+ case JSON_PASS:
+ fprintf(fp, "[\"%s\", ", CDATA0);
+ deeper = this->deeper;
+ if (deeper) {
+ vftp = the_vft[deeper->type];
+ vftp->generate(deeper, which, fp);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "node_complex_generate unimp pass %d...\n", which);
+ break;
+ }
+ current_is_complex--;
+}
+
+node_vft_t node_complex_vft = {
+ node_complex_print,
+ node_complex_generate,
+ 0,
+};
+
+void node_noversion_print (node_t *this)
+{
+ primtype_recursive_print (this, "noversion ");
+}
+
+void node_noversion_generate (node_t *this, enum passid which, FILE *ofp)
+{
+ fprintf(stderr, "node_noversion_generate called...\n");
+}
+
+node_vft_t node_noversion_vft = {
+ node_noversion_print,
+ node_noversion_generate,
+ 0,
+};
+
+void node_uword_print (node_t *this)
+{
+ primtype_recursive_print(this, "uword ");
+}
+
+void node_uword_generate (node_t *this, enum passid which, FILE *ofp)
+{
+ primtype_recursive_generate(this, which, ofp, "uword", "uword", "");
+}
+
+node_vft_t node_uword_vft = {
+ node_uword_print,
+ node_uword_generate,
+ "clib_net_to_host_uword",
+};
+
+node_vft_t *the_vft[NODE_N_TYPES] = {
+ &node_illegal_vft,
+ &node_u8_vft,
+ &node_u16_vft,
+ &node_u32_vft,
+ &node_u64_vft,
+ &node_i8_vft,
+ &node_i16_vft,
+ &node_i32_vft,
+ &node_i64_vft,
+ &node_f64_vft,
+ &node_packed_vft,
+ &node_define_vft,
+ &node_union_vft,
+ &node_scalar_vft,
+ &node_vector_vft,
+ &node_complex_vft,
+ &node_noversion_vft,
+ &node_uword_vft,
+};
+
+void *make_node (enum node_subclass type)
+{
+ node_t *rv;
+
+ rv = (node_t *) malloc (sizeof (*rv));
+ if (rv == 0) {
+ fprintf (stderr, "fatal: make_node out of memory\n");
+ exit (1);
+ }
+ bzero (rv, sizeof (*rv));
+ rv->type = type;
+ return ((void *) rv);
+}
+
+YYSTYPE deeper (YYSTYPE arg1, YYSTYPE arg2)
+{
+ node_t *np1 = (node_t *) arg1;
+ node_t *np2 = (node_t *) arg2;
+ node_t *hook_point;
+
+ hook_point = np1;
+
+ while (hook_point->deeper)
+ hook_point = hook_point->deeper;
+
+ hook_point->deeper = np2;
+ return (arg1);
+}
+
+YYSTYPE addpeer (YYSTYPE arg1, YYSTYPE arg2)
+{
+ node_t *np1 = (node_t *) arg1;
+ node_t *np2 = (node_t *) arg2;
+ node_t *hook_point;
+
+ hook_point = np1;
+
+ while (hook_point->peer)
+ hook_point = hook_point->peer;
+
+ hook_point->peer = np2;
+ return (arg1);
+}
+
+/*
+ * add_slist (stmt_list, stmt)
+ */
+
+YYSTYPE add_slist (YYSTYPE a1, YYSTYPE a2)
+{
+ if (a1 && a2)
+ return (addpeer(a1, a2));
+ else if(a1)
+ return(a1);
+ else
+ return(a2);
+}
+
+/*
+ * add_define (char *name, defn_list);
+ */
+YYSTYPE add_define (YYSTYPE a1, YYSTYPE a2)
+{
+ node_t *np;
+
+ np = make_node(NODE_DEFINE);
+ np->data[0] = a1;
+ np->data[3] = (void *) message_crc;
+ deeper((YYSTYPE)np, a2);
+ return ((YYSTYPE) np);
+}
+
+/*
+ * add_defbody (defn_list, new_defn)
+ */
+YYSTYPE add_defbody (YYSTYPE a1, YYSTYPE a2)
+{
+ return (addpeer(a1, a2));
+}
+
+/*
+ * add_primtype ([packed], primitive type, instance)
+ */
+
+YYSTYPE add_primtype (YYSTYPE a1, YYSTYPE a2, YYSTYPE a3)
+{
+ /* Hook instance to type node */
+ deeper (a1, a2);
+ if (a3) {
+ deeper(a1, a3);
+ }
+ return (a1);
+}
+
+/*
+ * add_complex(char *type_name, instance)
+ */
+
+YYSTYPE add_complex (YYSTYPE a1, YYSTYPE a2)
+{
+ node_t *np;
+
+ np = make_node(NODE_COMPLEX);
+ np->data[0] = (void *) a1;
+
+ deeper((YYSTYPE)np, a2);
+ return ((YYSTYPE) np);
+}
+
+/*
+ * add_union(char *type_name, definition)
+ */
+
+YYSTYPE add_union (YYSTYPE a1, YYSTYPE a2)
+{
+ node_t *np;
+
+ np = make_node(NODE_UNION);
+ np->data[0] = (void *) a1;
+
+ deeper((YYSTYPE)np, a2);
+ return ((YYSTYPE) np);
+}
+
+
+/*
+ * add_vector_vbl (node_t *variable, YYSTYPE size)
+ */
+
+YYSTYPE add_vector_vbl (YYSTYPE a1, YYSTYPE a2)
+{
+ node_t *np;
+
+ np = make_node(NODE_VECTOR);
+ np->data[0] = (void *) a1;
+ np->data[1] = (void *) a2;
+ return ((YYSTYPE) np);
+}
+
+/*
+ * add_vector_vbl (char *vector_name, char *vector_length_var)
+ */
+
+YYSTYPE add_variable_length_vector_vbl (YYSTYPE vector_name, YYSTYPE vector_length_var)
+{
+ node_t *np;
+
+ np = make_node(NODE_VECTOR);
+ np->data[0] = (void *) vector_name;
+ np->data[1] = (void *) 0; // vector size used for vpe.api.h generation (array of length zero)
+ np->data[2] = (void *) vector_length_var; // name of the variable that stores vector length
+ return ((YYSTYPE) np);
+}
+
+/*
+ * add_scalar_vbl (char *name)
+ */
+YYSTYPE add_scalar_vbl (YYSTYPE a1)
+{
+ node_t *np;
+
+ np = make_node(NODE_SCALAR);
+ np->data[0] = (void *) a1;
+ return ((YYSTYPE) np);
+}
+
+/*
+ * set_flags (int flags, msg(=0?))
+ */
+YYSTYPE set_flags(YYSTYPE a1, YYSTYPE a2)
+{
+ node_t *np;
+ int flags;
+
+ np = (node_t *)a2;
+ if (!np)
+ return(0);
+
+ flags = (int)(uword) a1;
+
+ np->flags |= flags;
+
+ /* Generate a foo_reply_t right here */
+ if (flags & NODE_FLAG_AUTOREPLY)
+ autoreply(np);
+
+ return (a2);
+}
+/*
+ * suppress_version
+ */
+YYSTYPE suppress_version (void)
+{
+ dont_output_version = 1;
+ return (0);
+}
+
+void dump(node_t *np)
+{
+ node_vft_t *vftp;
+
+ while (np) {
+ vftp = the_vft[np->type];
+ vftp->print(np);
+ np = np->peer;
+ }
+}
+
+char *fixup_input_filename(void)
+{
+ char *cp;
+
+ cp = (char *)input_filename;
+
+ while (*cp)
+ cp++;
+
+ cp--;
+
+ while (cp > input_filename && *cp != '/')
+ cp--;
+ if (*cp == '/')
+ cp++;
+
+ strncpy (tmpbuf, cp, sizeof(tmpbuf)-1);
+
+ cp = tmpbuf;
+
+ while (*cp)
+ cp++;
+
+ cp--;
+
+ while (cp > tmpbuf && *cp != '.')
+ cp--;
+
+ if (*cp == '.')
+ *cp = 0;
+
+ return (sxerox(tmpbuf));
+}
+
+void generate_top_boilerplate(FILE *fp)
+
+{
+ time_t curtime;
+ char *datestring;
+ char *source_date_epoch;
+ if ((source_date_epoch = getenv("SOURCE_DATE_EPOCH")) == NULL || (curtime = (time_t)strtol(source_date_epoch, NULL, 10)) <= 0)
+ curtime = starttime;
+ datestring = asctime(gmtime(&curtime));
+ fixed_name = fixup_input_filename();
+
+ datestring[24] = 0;
+
+ fprintf (fp, "/*\n");
+ fprintf (fp, " * VLIB API definitions %s\n", datestring);
+ fprintf (fp, " * Input file: %s\n", input_filename);
+ fprintf (fp, " * Automatically generated: please edit the input file ");
+ fprintf (fp, "NOT this file!\n");
+ fprintf (fp, " */\n\n");
+ fprintf (fp, "#if defined(vl_msg_id)||defined(vl_union_id)||");
+ fprintf (fp, "defined(vl_printfun) \\\n ||defined(vl_endianfun)||");
+ fprintf (fp, " defined(vl_api_version)||defined(vl_typedefs) \\\n");
+ fprintf (fp, " ||defined(vl_msg_name)||defined(vl_msg_name_crc_list)\n");
+ fprintf (fp, "/* ok, something was selected */\n");
+ fprintf (fp, "#else\n");
+ fprintf (fp, "#warning no content included from %s\n", input_filename);
+ fprintf (fp, "#endif\n\n");
+ fprintf (fp, "#define VL_API_PACKED(x) x __attribute__ ((packed))\n\n");
+}
+
+void generate_bottom_boilerplate(FILE *fp)
+
+{
+ fprintf (fp, "\n#ifdef vl_api_version\n");
+
+ if (dont_output_version) {
+ fprintf (fp, "/* WARNING: API FILE VERSION CHECK DISABLED */\n");
+ input_crc = 0;
+ }
+
+ fprintf (fp, "vl_api_version(%s, 0x%08x)\n\n",
+ fixed_name, (unsigned int)input_crc);
+ fprintf (fp, "#endif\n\n");
+}
+
+void generate_msg_ids(YYSTYPE a1, FILE *fp)
+{
+ node_t *np = (node_t *)a1;
+
+ fprintf (fp, "\n/****** Message ID / handler enum ******/\n\n");
+ fprintf (fp, "#ifdef vl_msg_id\n");
+
+ while (np) {
+ if (np->type == NODE_DEFINE) {
+ if (!(np->flags & NODE_FLAG_TYPEONLY)) {
+ fprintf (fp, "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n",
+ uppercase(np->data[0]), (i8 *)np->data[0]);
+ } else {
+ fprintf (fp, "/* typeonly: %s */\n", (i8 *)np->data[0]);
+ }
+ }
+ np = np->peer;
+ }
+ fprintf (fp, "#endif\n");
+
+}
+
+void generate_msg_names(YYSTYPE a1, FILE *fp)
+{
+ node_t *np = (node_t *)a1;
+
+ fprintf (fp, "\n/****** Message names ******/\n\n");
+
+ fprintf (fp, "#ifdef vl_msg_name\n");
+
+ while (np) {
+ if (np->type == NODE_DEFINE) {
+ if (!(np->flags & NODE_FLAG_TYPEONLY)) {
+ fprintf (fp, "vl_msg_name(vl_api_%s_t, %d)\n",
+ (i8 *) np->data[0],
+ (np->flags & NODE_FLAG_DONT_TRACE ? 0 : 1));
+ } else {
+ fprintf (fp, "/* typeonly: %s */\n", (i8 *)np->data[0]);
+ }
+ }
+ np = np->peer;
+ }
+ fprintf (fp, "#endif\n\n");
+}
+
+void generate_msg_name_crc_list (YYSTYPE a1, FILE *fp)
+{
+ node_t *np = (node_t *)a1;
+ char *unique_suffix, *cp;
+
+ unique_suffix = sxerox(fixed_name);
+
+ cp = unique_suffix;
+ while (*cp && (*cp != '.'))
+ cp++;
+ if (*cp == '.')
+ *cp = 0;
+
+ fprintf (fp, "\n/****** Message name, crc list ******/\n\n");
+
+ fprintf (fp, "#ifdef vl_msg_name_crc_list\n");
+ fprintf (fp, "#define foreach_vl_msg_name_crc_%s ", unique_suffix);
+
+ while (np) {
+ if (np->type == NODE_DEFINE) {
+ if (!(np->flags & NODE_FLAG_TYPEONLY)) {
+ fprintf (fp, "\\\n_(VL_API_%s, %s, %08x) ",
+ uppercase (np->data[0]), (i8 *) np->data[0],
+ (u32)(uword)np->data[3]);
+ }
+ }
+ np = np->peer;
+ }
+ fprintf (fp, "\n#endif\n\n");
+ free (unique_suffix);
+}
+
+void generate_typedefs(YYSTYPE a1, FILE *fp)
+{
+ node_t *np = (node_t *)a1;
+ node_vft_t *vftp;
+
+ fprintf(fp, "\n/****** Typedefs *****/\n\n");
+ fprintf(fp, "#ifdef vl_typedefs\n\n");
+
+ /* Walk the top-level node-list */
+ while (np) {
+ if (np->type == NODE_DEFINE) {
+ /* Yeah, this is pedantic */
+ vftp = the_vft[np->type];
+ vftp->generate(np, TYPEDEF_PASS, fp);
+ }
+ np = np->peer;
+ }
+ fprintf(fp, "#endif /* vl_typedefs */\n\n");
+}
+
+void union_walk_one_defn(node_t *np, FILE *fp)
+{
+ node_t *vblp;
+ node_t *uelem;
+
+ /* Walk the list of typed objects in this msg def */
+ while (np) {
+ if (np->type == NODE_UNION) {
+ current_union_name = np->data[0];
+ uelem = np->deeper;
+
+ /* Walk the list of objects in this union */
+ while (uelem) {
+ vblp = uelem->deeper;
+ /* Drill down on each element, find the variable name */
+ while(vblp) {
+ if (vblp->type == NODE_SCALAR ||
+ vblp->type == NODE_VECTOR ||
+ vblp->type == NODE_COMPLEX) {
+ fprintf(ofp, "#define %s_",
+ uppercase(current_def_name));
+ fprintf(ofp, "%s_", uppercase(current_union_name));
+ fprintf(ofp, "%s %d\n",uppercase(vblp->data[0]),
+ current_id);
+ current_id++;
+ break;
+ }
+ vblp = vblp->deeper;
+ }
+ uelem = uelem->peer;
+ }
+ current_union_name = 0;
+ current_id = 1;
+ }
+ np = np->peer;
+ }
+}
+
+void generate_uniondefs(YYSTYPE a1, FILE *fp)
+{
+ node_t *np = (node_t *)a1;
+
+ fprintf(fp, "/****** Discriminated Union Definitions *****/\n\n");
+ fprintf(fp, "#ifdef vl_union_id\n\n");
+
+ /* Walk the top-level node-list */
+ while (np) {
+ if (np->type == NODE_DEFINE) {
+ current_id = 1;
+ current_def_name = np->data[0];
+ union_walk_one_defn(np->deeper, fp);
+ }
+ np = np->peer;
+ }
+ fprintf(fp, "\n#endif /* vl_union_id */\n\n");
+}
+
+void generate_printfun(YYSTYPE a1, FILE *fp)
+{
+ node_t *np = (node_t *)a1;
+ node_vft_t *vftp;
+
+ fprintf(fp, "/****** Print functions *****/\n\n");
+ fprintf(fp, "#ifdef vl_printfun\n\n");
+
+ fprintf(fp, "#ifdef LP64\n");
+ fputs ("#define _uword_fmt \"%lld\"\n", fp);
+ fputs ("#define _uword_cast (long long)\n", fp);
+ fprintf(fp, "#else\n");
+ fputs("#define _uword_fmt \"%ld\"\n", fp);
+ fputs ("#define _uword_cast long\n", fp);
+ fprintf(fp, "#endif\n\n");
+
+ /* Walk the top-level node-list */
+ while (np) {
+ if (np->type == NODE_DEFINE) {
+ if (!(np->flags & NODE_FLAG_MANUAL_PRINT)) {
+ fprintf(fp,
+ "static inline void *vl_api_%s_t_print (vl_api_%s_t *a,",
+ (i8 *)np->data[0], (i8 *) np->data[0]);
+ fprintf(fp, "void *handle)\n{\n");
+ /* output the message name */
+ fprintf(fp,
+ " vl_print(handle, \"vl_api_%s_t:\\n\");\n",
+ (i8 *)np->data[0]);
+
+ indent += 4;
+ /* Yeah, this is pedantic */
+ vftp = the_vft[np->type];
+ vftp->generate(np, PRINTFUN_PASS, fp);
+ fprintf(fp, " return handle;\n");
+ fprintf(fp, "}\n\n");
+ indent -= 4;
+ } else {
+ fprintf(fp, "/***** manual: vl_api_%s_t_print *****/\n\n",
+ (i8 *) np->data[0]);
+ }
+ }
+ np = np->peer;
+ }
+ fprintf(fp, "#endif /* vl_printfun */\n\n");
+}
+
+void generate_endianfun(YYSTYPE a1, FILE *fp)
+{
+ node_t *np = (node_t *)a1;
+ node_vft_t *vftp;
+
+ fprintf(fp, "\n/****** Endian swap functions *****/\n\n");
+ fprintf(fp, "#ifdef vl_endianfun\n\n");
+ fprintf(fp, "#undef clib_net_to_host_uword\n");
+ fprintf(fp, "#ifdef LP64\n");
+ fprintf(fp, "#define clib_net_to_host_uword clib_net_to_host_u64\n");
+ fprintf(fp, "#else\n");
+ fprintf(fp, "#define clib_net_to_host_uword clib_net_to_host_u32\n");
+ fprintf(fp, "#endif\n\n");
+
+ /* Walk the top-level node-list */
+ while (np) {
+ if (np->type == NODE_DEFINE) {
+ if (!(np->flags & NODE_FLAG_MANUAL_ENDIAN)) {
+ fprintf(fp,
+ "static inline void vl_api_%s_t_endian (vl_api_%s_t *a)\n{\n",
+ (i8 *) np->data[0], (i8 *) np->data[0]);
+ indent += 4;
+ /* Yeah, this is pedantic */
+ vftp = the_vft[np->type];
+ vftp->generate(np, ENDIANFUN_PASS, fp);
+ fprintf(fp, "}\n\n");
+ indent -= 4;
+ } else {
+ fprintf(fp, "/***** manual: vl_api_%s_t_endian *****/\n\n",
+ (i8 *) np->data[0]);
+ }
+ }
+ np = np->peer;
+ }
+ fprintf(fp, "#endif /* vl_endianfun */\n\n");
+}
+
+void add_msg_ids(YYSTYPE a1)
+{
+ node_t *np = (node_t *)a1;
+ node_t *new_u16;
+ node_t *new_vbl;
+
+ /* Walk the top-level node-list */
+ while (np) {
+ if (np->type == NODE_DEFINE) {
+ if (!(np->flags & NODE_FLAG_TYPEONLY)) {
+ /* add the parse tree for "u16 _vl_msg_id" */
+ new_u16 = make_node(NODE_U16);
+ new_u16->peer = np->deeper;
+ np->deeper = new_u16;
+ new_vbl = make_node(NODE_SCALAR);
+ new_vbl->data[0] = sxerox("_vl_msg_id");
+ new_u16->deeper = new_vbl;
+ }
+ }
+ np = np->peer;
+ }
+}
+
+void generate_python_msg_definitions(YYSTYPE a1, FILE *fp)
+{
+ node_t *np = (node_t *)a1;
+ node_vft_t *vftp;
+ fprintf (fp, "messages = [\n");
+ /* Walk the top-level node-list */
+ while (np) {
+ if (np->type == NODE_DEFINE && !(np->flags & NODE_FLAG_TYPEONLY)) {
+ /* Yeah, this is pedantic */
+ vftp = the_vft[np->type];
+ vftp->generate(np, PYTHON_PASS, fp);
+ }
+ np = np->peer;
+ }
+ fprintf (fp, "\n]\n");
+}
+
+static bool
+is_typeonly_check(node_t *np, bool typeonly)
+{
+ bool is_typeonly = (np->flags & NODE_FLAG_TYPEONLY);
+ return (is_typeonly == typeonly);
+}
+
+static void
+generate_json_definitions(YYSTYPE a1, FILE *fp, bool typeonly)
+{
+ node_t *np = (node_t *)a1;
+ node_vft_t *vftp;
+ indent_me(fp);
+ if (typeonly)
+ fprintf (fp, "\"types\" : [\n");
+ else
+ fprintf (fp, "\"messages\" : [\n");
+
+ /* Walk the top-level node-list */
+ bool comma = false;
+ indent += 4;
+ while (np) {
+ if (np->type == NODE_DEFINE && is_typeonly_check(np, typeonly)) {
+ /* Yeah, this is pedantic */
+ vftp = the_vft[np->type];
+ indent_me(fp);
+ vftp->generate(np, JSON_PASS, fp);
+ comma = true;
+ }
+ np = np->peer;
+ if (comma && np &&
+ np->type == NODE_DEFINE && is_typeonly_check(np, typeonly))
+ fprintf (fp, ",\n");
+
+ }
+ indent -= 4;
+ fprintf (fp, "\n");
+ indent_me(fp);
+ fprintf(fp, "]");
+}
+
+void generate_python_typeonly_definitions(YYSTYPE a1, FILE *fp)
+{
+ node_t *np = (node_t *)a1;
+ node_vft_t *vftp;
+ fprintf (fp, "types = [\n");
+ /* Walk the top-level node-list */
+ while (np) {
+ if (np->type == NODE_DEFINE && (np->flags & NODE_FLAG_TYPEONLY)) {
+ vftp = the_vft[np->type];
+ vftp->generate(np, PYTHON_PASS, fp);
+ }
+ np = np->peer;
+ }
+ fprintf (fp, "\n]\n");
+}
+
+void generate_python(YYSTYPE a1, FILE *fp)
+{
+ generate_python_typeonly_definitions(a1, fp);
+ generate_python_msg_definitions(a1, fp);
+
+ /*
+ * API CRC signature
+ */
+ fprintf (fp, "vl_api_version = 0x%08x\n\n", (unsigned int)input_crc);
+}
+
+void generate_json(YYSTYPE a1, FILE *fp)
+{
+ fprintf (fp, "{\n");
+ indent += 4;
+ generate_json_definitions(a1, fp, true);
+ fprintf (fp, ",\n");
+ generate_json_definitions(a1, fp, false);
+
+ /*
+ * API CRC signature
+ */
+ fprintf (fp, ",\n\"vl_api_version\" :\"0x%08x\"\n",
+ (unsigned int)input_crc);
+ fprintf (fp, "}\n");
+}
+
+void generate(YYSTYPE a1)
+{
+ if (dump_tree) {
+ dump((node_t *)a1);
+ }
+
+ add_msg_ids(a1);
+
+ if (ofp) {
+ generate_top_boilerplate(ofp);
+
+ generate_msg_ids(a1, ofp);
+ generate_msg_names(a1, ofp);
+ generate_msg_name_crc_list(a1, ofp);
+ generate_typedefs(a1, ofp);
+ generate_uniondefs(a1, ofp);
+ generate_printfun(a1, ofp);
+ generate_endianfun(a1, ofp);
+
+ generate_bottom_boilerplate(ofp);
+ }
+ if (pythonfp) {
+ generate_python(a1, pythonfp);
+ }
+ if (jsonfp) {
+ generate_json(a1, jsonfp);
+ }
+}
diff --git a/src/tools/vppapigen/node.h b/src/tools/vppapigen/node.h
new file mode 100644
index 00000000..65bd5d10
--- /dev/null
+++ b/src/tools/vppapigen/node.h
@@ -0,0 +1,96 @@
+/*
+ *------------------------------------------------------------------
+ * node.h - definitions for an API generator
+ *
+ * Copyright (c) 2004-2009 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 _node_h_
+#define _node_h_
+
+/*
+ * Global prototypes
+ */
+
+char *sxerox (const char *s);
+
+enum node_subclass { /* WARNING: indices must match the vft... */
+ NODE_ILLEGAL=0,
+ NODE_U8,
+ NODE_U16,
+ NODE_U32,
+ NODE_U64,
+ NODE_I8,
+ NODE_I16,
+ NODE_I32,
+ NODE_I64,
+ NODE_F64,
+ NODE_PACKED,
+ NODE_DEFINE,
+ NODE_UNION,
+ NODE_SCALAR,
+ NODE_VECTOR,
+ NODE_COMPLEX,
+ NODE_NOVERSION,
+ NODE_UWORD,
+ NODE_N_TYPES, /* number of node types with VFT's */
+
+ /* pseudo-node(s) used in the lexer keyword table, but
+ NOT in need of a VFT... */
+ NODE_TYPEONLY,
+ NODE_MANUAL_PRINT,
+ NODE_MANUAL_ENDIAN,
+ NODE_DONT_TRACE,
+ NODE_AUTOREPLY,
+};
+
+enum passid {
+ TYPEDEF_PASS=1,
+ UNION_DEF_PASS,
+ ENDIANFUN_PASS,
+ PRINTFUN_PASS,
+ PYTHON_PASS,
+ JSON_PASS,
+};
+
+extern void *make_node (enum node_subclass type);
+
+typedef struct node_ {
+ enum node_subclass type;
+ struct node_ *peer;
+ struct node_ *deeper;
+ int flags;
+ void *data[4];
+} node_t;
+
+/* To shut up gcc-4.2.x warnings */
+#define CDATA0 ((char *)(this->data[0]))
+#define IDATA1 ((int)(uword)(this->data[1]))
+#define CDATA2 ((char *)(this->data[2]))
+#define CDATA3 ((char *)(this->data[3]))
+
+#define NODE_FLAG_MANUAL_PRINT (1<<0)
+#define NODE_FLAG_MANUAL_ENDIAN (1<<1)
+#define NODE_FLAG_TYPEONLY (1<<3)
+#define NODE_FLAG_DONT_TRACE (1<<4)
+#define NODE_FLAG_AUTOREPLY (1<<5)
+
+typedef struct node_vft_ {
+ void (*print)(struct node_ *);
+ void (*generate)(struct node_ *, enum passid id, FILE *ofp);
+ char *endian_converter;
+} node_vft_t;
+
+#endif /* _node_h */