diff options
author | Damjan Marion <damarion@cisco.com> | 2016-12-19 23:05:39 +0100 |
---|---|---|
committer | Damjan Marion <damarion@cisco.com> | 2016-12-28 12:25:14 +0100 |
commit | 7cd468a3d7dee7d6c92f69a0bb7061ae208ec727 (patch) | |
tree | 5de62f8dbd3a752f5a676ca600e43d2652d1ff1a /src/tools | |
parent | 696f1adec0df3b8f161862566dd9c86174302658 (diff) |
Reorganize source tree to use single autotools instance
Change-Id: I7b51f88292e057c6443b12224486f2d0c9f8ae23
Signed-off-by: Damjan Marion <damarion@cisco.com>
Diffstat (limited to 'src/tools')
36 files changed, 14621 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..6454c84d --- /dev/null +++ b/src/tools/g2/clib.c @@ -0,0 +1,154 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2009-2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/fcntl.h> +#include <sys/mman.h> +#include <unistd.h> +#include <ctype.h> +#include <vppinfra/clib.h> +#include <vppinfra/vec.h> +#include <vppinfra/hash.h> +#include <vppinfra/elog.h> +#include <pwd.h> +#include <stdarg.h> +#include <time.h> +#include "cpel.h" +#include "g2.h" + +int widest_track_format; + +typedef struct bound_track_ { + u32 track; + u8 *track_str; +} bound_track_t; + +bound_track_t *bound_tracks; + +uword *the_evtdef_hash; /* (event-id, event-definition) hash */ +uword *the_trackdef_hash; /* (track-id, track-definition) hash */ + +elog_main_t elog_main; + +void *get_clib_event (unsigned int datum) +{ + elog_event_t *ep = vec_elt_at_index (elog_main.events, datum); + return (void *)ep; +} + +/* + * read_clib_file + */ +int read_clib_file(char *clib_file) +{ + static FILE *ofp; + clib_error_t *error = 0; + int i; + elog_main_t *em = &elog_main; + double starttime, delta; + + vec_free(em->events); + vec_free(em->event_types); + if (the_trackdef_hash) + hash_free(the_trackdef_hash); + + the_trackdef_hash = hash_create (0, sizeof (uword)); + + error = elog_read_file (&elog_main, clib_file); + + if (error) { + fformat(stderr, "%U", format_clib_error, error); + return (1); + } + + if (ofp == NULL) { + ofp = fdopen(2, "w"); + if (ofp == NULL) { + fprintf(stderr, "Couldn't fdopen(2)?\n"); + exit(1); + } + } + + em = &elog_main; + + for (i = 0; i < vec_len (em->tracks); i++) { + u32 track_code; + bound_track_t * btp; + elog_track_t * t; + uword * p; + int track_strlen; + + t = &em->tracks[i]; + track_code = i; + p = hash_get(the_trackdef_hash, track_code); + if (p) { + fprintf(ofp, "track %d redefined, retain first definition\n", + track_code); + continue; + } + vec_add2(bound_tracks, btp, 1); + btp->track = track_code; + btp->track_str = (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(); + + em->events = elog_get_events (em); + + cpel_event_init(vec_len(em->events)); + + starttime = em->events[0].time; + + for (i = 0; i < vec_len (em->events); i++) { + elog_event_t *ep; + + ep = vec_elt_at_index(em->events, i); + + delta = ep->time - starttime; + + add_clib_event (delta, ep->track, ep->type + 1, i); + } + + cpel_event_finalize(); + + set_pid_ax_width(8*widest_track_format); + + return(0); +} diff --git a/src/tools/g2/configure.ac b/src/tools/g2/configure.ac new file mode 100644 index 00000000..c8af7747 --- /dev/null +++ b/src/tools/g2/configure.ac @@ -0,0 +1,12 @@ +AC_INIT(g2, 3.0) +AM_INIT_AUTOMAKE +AM_SILENT_RULES([yes]) + +AC_CHECK_LIB([vppinfra], [clib_mem_get_page_size],, + AC_MSG_ERROR([Please install the vpp-lib package])) +AC_CHECK_HEADER([vppinfra/clib.h],, + AC_MSG_ERROR([Please install the vpp-dev package])) + +PKG_CHECK_MODULES(g2, gtk+-2.0) + +AC_OUTPUT([Makefile]) 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..d4333bb0 --- /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_malloc(sizeof(pid_sort_t)*g_npids); + + for (i = 0; i < PIDHASH_NBUCKETS; i++) { + pp = s_pidhash[i]; + while(pp) { + psp->pid = pp; + psp->pid_value = pp->pid_value; + psp++; + pp = pp->next; + } + } + + qsort(&g_pids[0], g_npids, sizeof(pid_sort_t), pid_cmp); + + /* put the sort order into the pid objects */ + psp = g_pids; + + /* + * This is rather gross. + * + * We happen to know that whenever this function is called, the hash table + * structure itself is immediately torn down. So the "next" pointers in the + * pid_data_t elements are about to become useless. + * + * So we re-use them, to link all the pid_data_t elements together into a + * single unified linked list, with g_pid_data_list pointing to the head. + * This means we can walk all the pid_data_t objects if we really want to. + * Reading snapshots from disk is one example. + * + * Alternatively we could just leave the hash table in place; this is + * far nicer, but as it happens, trading O(n) lookups for O(1) lookups + * isn't actually a problem for the restricted post-tear-down usage. So for + * now we take the memory savings and swap our hash table for a list. + */ + p_previous = &g_pid_data_list; + for (i = 0; i < g_npids; i++) { + pp = psp->pid; + pp->pid_index = i; + *p_previous = pp; + p_previous = &pp->next; + psp++; + } + *p_previous = NULL; + + /* + * Squirrel away original (sorted) vector, so we can + * toggle between "chase" mode, snapshots, and the original + * display method on short notice + */ + g_original_pids = g_malloc(sizeof(pid_sort_t)*g_npids); + memcpy (g_original_pids, g_pids, sizeof(pid_sort_t)*g_npids); +} + +/**************************************************************************** +* read_events +****************************************************************************/ + +void read_events(char *filename) +{ + ulong *ulp; + ulong size; + event_t *ep; + raw_event_t *rep; + ulonglong start_time=0ULL; + ulonglong low_time; + boolean once=TRUE; + int i; + char tmpbuf [128]; + + ulp = (ulong *)mapfile(filename, &size); + + if (ulp == NULL) { + sprintf(tmpbuf, "Couldn't open %s\n", filename); + infobox("Read Event Log Failure", tmpbuf); + return; + } + + g_nevents = ntohl(*ulp); + + if (size != (g_nevents*sizeof(raw_event_t) + sizeof(g_nevents))) { + sprintf(tmpbuf, "%s was damaged, or isn't an event log.\n", filename); + infobox("Bad Input File", tmpbuf); + g_nevents = 0; + unmapfile((char *)ulp, size); + return; + } + + rep = (raw_event_t *)(ulp+1); + + if (g_events) + g_free(g_events); + + g_events = (event_t *)g_malloc(g_nevents * sizeof(event_t)); + ep = g_events; + + while (g_npids > 0) { + g_free((g_pids + g_npids-1)->pid); + g_npids--; + } + if (g_pids) { + g_free(g_pids); + g_free(g_original_pids); + g_pids = 0; + g_original_pids = 0; + } + + s_pidhash = (pid_data_t **)g_malloc0( + PIDHASH_NBUCKETS*sizeof(pid_data_t *)); + + /* $$$ add a SEGV handler... */ + for (i = 0; i < g_nevents; i++) { + if (once) { + once = FALSE; + start_time = ((ulonglong)ntohl(rep->time[0])); + start_time <<= 32; + low_time = ntohl(rep->time[1]); + low_time &= 0xFFFFFFFF; + start_time |= low_time; + ep->time = 0LL; + } else { + ep->time = ((ulonglong)ntohl(rep->time[0])); + ep->time <<= 32; + low_time = ntohl(rep->time[1]); + low_time &= 0xFFFFFFFF; + ep->time |= low_time; + ep->time -= start_time; + ep->time /= ticks_per_ns; + } + ep->code = ntohl(rep->code); + ep->pid = find_or_add_pid(ntohl(rep->pid)); + ep->datum = ntohl(rep->datum); + ep->flags = 0; + ep++; + rep++; + } + + unmapfile((char *)ulp, size); + + make_sorted_pid_vector(); + g_free(s_pidhash); + s_pidhash = 0; + + /* Give the view-1 world a chance to reset a few things... */ + view1_read_events_callback(); +} + +static event_t *add_ep; + +/**************************************************************************** +* cpel_event_init +****************************************************************************/ +void cpel_event_init (ulong nevents) +{ + g_nevents = nevents; + if (g_events) + g_free(g_events); + add_ep = g_events = (event_t *)g_malloc(g_nevents * sizeof(event_t)); + while (g_npids > 0) { + g_free((g_pids + g_npids-1)->pid); + g_npids--; + } + if (g_pids) { + g_free(g_pids); + g_free(g_original_pids); + g_pids = 0; + g_original_pids = 0; + } + s_pidhash = (pid_data_t **)g_malloc0( + PIDHASH_NBUCKETS*sizeof(pid_data_t *)); +} + +/**************************************************************************** +* add_cpel_event +****************************************************************************/ + +void add_cpel_event(ulonglong delta, ulong track, ulong event, ulong datum) +{ + event_t *ep; + + ep = add_ep++; + ep->time = delta; + ep->pid = find_or_add_pid(track); + ep->code = event; + ep->datum = datum; + ep->flags = 0; +} + +/**************************************************************************** +* add_clib_event +****************************************************************************/ + +void add_clib_event(double delta, unsigned short track, + unsigned short event, unsigned int index) +{ + event_t *ep; + + ep = add_ep++; + ep->time = (ulonglong) (delta * 1e9); /* time in intger nanoseconds */ + ep->pid = find_or_add_pid(track); + ep->code = event; + ep->datum = index; + ep->flags = EVENT_FLAG_CLIB; +} + +/**************************************************************************** +* cpel_event_finalize +****************************************************************************/ + +void cpel_event_finalize(void) +{ + make_sorted_pid_vector(); + g_free(s_pidhash); + s_pidhash = 0; + + /* Give the view-1 world a chance to reset a few things... */ + view1_read_events_callback(); +} + +/**************************************************************************** +* mapfile +****************************************************************************/ + +char *mapfile (char *file, ulong *sizep) +{ + struct stat statb; + char *rv; + int maphfile; + size_t mapfsize; + + maphfile = open (file, O_RDONLY); + + if (maphfile < 0) + return (NULL); + + if (fstat (maphfile, &statb) < 0) { + return (NULL); + } + + /* Don't try to mmap directories, FIFOs, semaphores, etc. */ + if (! (statb.st_mode & S_IFREG)) { + return (NULL); + } + + mapfsize = statb.st_size; + + if (mapfsize < 3) { + close (maphfile); + return (NULL); + } + + rv = mmap (0, mapfsize, PROT_READ, MAP_SHARED, maphfile, 0); + + if (rv == 0) { + g_error ("%s mapping problem, I quit...\n", file); + } + + close (maphfile); + + if (madvise (rv, mapfsize, MADV_SEQUENTIAL) < 0) { + return (rv); + } + + if (sizep) { + *sizep = mapfsize; + } + return (rv); +} + +/**************************************************************************** +* unmapfile +****************************************************************************/ + +boolean unmapfile (char *addr, ulong size) +{ + if (munmap (addr, size) < 0) { + g_warning("Unmap error, addr 0x%lx size 0x%x\n", + (unsigned long) addr, (unsigned int)size); + return(FALSE); + } + return(TRUE); +} + +/**************************************************************************** +* find_event_index +* Binary search for first event whose time is >= t +****************************************************************************/ + +int find_event_index (ulonglong t) +{ + int index, bottom, top; + event_t *ep; + + bottom = g_nevents-1; + top = 0; + + while (1) { + index = (bottom + top) / 2; + + ep = (g_events + index); + + if (ep->time == t) + return(index); + + if (top >= bottom) { + while (index > 0 && ep->time > t) { + ep--; + index--; + } + while (index < g_nevents && ep->time < t) { + ep++; + index++; + } + return(index); + } + + if (ep->time < t) + top = index + 1; + else + bottom = index - 1; + } +} + +/**************************************************************************** +* events_about +****************************************************************************/ + +void events_about (char *tmpbuf) +{ + sprintf(tmpbuf+strlen(tmpbuf), "%d total events, %.3f ticks per us\n", + (int)g_nevents, ticks_per_ns); +} diff --git a/src/tools/g2/g2.h b/src/tools/g2/g2.h new file mode 100644 index 00000000..1ab42191 --- /dev/null +++ b/src/tools/g2/g2.h @@ -0,0 +1,195 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2005-2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * typedefs and so forth + */ +#include <sys/types.h> +#include <gtk-2.0/gtk/gtk.h> +#include <stdio.h> +#include "props.h" + +typedef char boolean; +typedef unsigned long long ulonglong; + +/* + * main.c + */ + +GtkWidget *g_mainwindow; +GtkWidget *g_mainvbox; +GtkWidget *g_mainhbox; + +/* + * pointsel.c + */ +void point_selector_init(void); +boolean read_event_definitions (char *filename); +char *sxerox(char *); +void pointsel_about(char *); +void pointsel_next_snapshot(void); +void initialize_events(void); +void finalize_events(void); + +#define NEVENTS 100000 + +typedef struct event_def_ { + ulong event; + char *name; + char *format; + boolean selected; + boolean is_clib; + char pad[2]; +} event_def_t; + +event_def_t *find_event_definition (ulong code); + +event_def_t g_eventdefs[NEVENTS]; + +/* + * config params + */ +int c_maxpointsel; /* max # points shown in selector dlg */ +gint c_view1_draw_width; +gint c_view1_draw_height; + +/* + * menu1.c + */ + +void menu1_init(void); +void modal_dialog (char *label_text, char *retry_text, char *default_value, + boolean (*cb)(char *)); +void infobox(char *label_text, char *text); +/* + * view1.c + */ +GdkFont *g_font; +GdkColor fg_black, bg_white; +void view1_init(void); +void view1_display(void); +void view1_read_events_callback(void); +void view1_display_when_idle(void); +void view1_print_callback(GtkToggleButton *item, gpointer data); +void view1_about(char *); +void set_pid_ax_width(int width); +void set_window_title(const char *filename); + +enum view1_tbox_fn { + TBOX_DRAW_BOXED = 1, /* note: order counts */ + TBOX_DRAW_EVENT, + TBOX_DRAW_PLAIN, + TBOX_PRINT_BOXED, + TBOX_PRINT_EVENT, + TBOX_PRINT_PLAIN, /* end restriction */ + TBOX_GETRECT_BOXED, + TBOX_GETRECT_EVENT, + TBOX_GETRECT_PLAIN, +}; + +enum view1_line_fn { + LINE_DRAW_BLACK = 1, + LINE_DRAW_WHITE, + LINE_PRINT, +}; + +GdkRectangle *tbox (char *s, int x, int y, enum view1_tbox_fn function); +void line (int x1, int y1, int x2, int y2, enum view1_line_fn function); +gint view1_handle_key_press_event (GtkWidget *widget, GdkEventKey *event); + +/* + * events.c + */ + +void events_about (char *); + +typedef struct raw_event { + unsigned long time[2]; + unsigned long pid; + unsigned long code; + unsigned long datum; +} raw_event_t; + +void event_init(void); +char *mapfile (char *file, ulong *sizep); +boolean unmapfile (char *addr, ulong size); +void read_events (char *); +int find_event_index (ulonglong t); +int read_cpel_file(char *file); +int read_clib_file(char *file); +void cpel_event_init(ulong); +void add_event_from_cpel_file(ulong, char * , char *); +void add_event_from_clib_file(unsigned int event, char *name, + unsigned int vec_index); +void add_cpel_event(ulonglong delta, ulong, ulong, ulong); +void add_clib_event(double delta, unsigned short track, + unsigned short event, unsigned int index); +void cpel_event_finalize(void); +void *get_clib_event (unsigned int datum); + +typedef struct pid_data { + struct pid_data *next; + ulong pid_value; /* The actual pid value */ + ulong pid_index; /* Index in pid sort order */ +} pid_data_t; + +#define EVENT_FLAG_SELECT 0x00000001 /* This event is selected */ +#define EVENT_FLAG_SEARCHRSLT 0x00000002 /* This event is the search rslt */ +#define EVENT_FLAG_CLIB 0x00000004 /* clib event */ + +typedef struct pid_sort { + struct pid_data *pid; + ulong pid_value; + /* + * This is a bit of a hack, since this is used only by the view: + */ + unsigned color_index; +} pid_sort_t; + +typedef struct event { + ulonglong time; + ulong code; + pid_data_t *pid; + ulong datum; + ulong flags; +} event_t; + + +boolean g_little_endian; +event_t *g_events; +ulong g_nevents; +pid_sort_t *g_pids; +pid_sort_t *g_original_pids; +int g_npids; +pid_data_t *g_pid_data_list; + +#define PIDHASH_NBUCKETS 20021 /* Should be prime */ + +boolean ticks_per_ns_set; +double ticks_per_ns; + +/* + * version.c + */ +const char *version_string; +const char *minor_v_string; + +/* + * cpel.c + */ +char *get_track_label(unsigned long); +int widest_track_format; +char *strtab_ref(unsigned long); diff --git a/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..a782e17f --- /dev/null +++ b/src/tools/g2/main.c @@ -0,0 +1,196 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2005-2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "g2.h" +#include "props.h" +#include <pwd.h> +#include <strings.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +/* + * globals + */ + +GtkWidget *g_mainwindow; /* The main window */ + +/* Graphical object heirarchy + * + * [main window] + * [main vbox] + * [main (e.g. file) menubar] + * [view hbox] + * [view bottom menu] + */ + +GtkWidget *g_mainvbox; +GtkWidget *g_mainhbox; + +gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) +{ + /* Allow window to be destroyed */ + return(FALSE); +} + +void destroy(GtkWidget *widget, gpointer data) +{ + gtk_main_quit(); +} + +int main (int argc, char **argv) +{ + char tmpbuf [128]; + struct passwd *pw; + char *event_file = 0; + char *cpel_file = 0; + char *clib_file =0; + char *title = "none"; + int curarg=1; + char *homedir; + + gtk_init(&argc, &argv); + + homedir = getenv ("HOME"); + tmpbuf[0] = 0; + + if (homedir) { + sprintf(tmpbuf, "%s/.g2", homedir); + } else { + pw = getpwuid(geteuid()); + if (pw) { + sprintf(tmpbuf, "%s/.g2", pw->pw_dir); + } + } + if (tmpbuf[0]) + readprops(tmpbuf); + + g_mainwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + gtk_signal_connect (GTK_OBJECT(g_mainwindow), "delete_event", + GTK_SIGNAL_FUNC (delete_event), NULL); + + gtk_signal_connect (GTK_OBJECT(g_mainwindow), "destroy", + GTK_SIGNAL_FUNC (destroy), NULL); + + gtk_container_set_border_width(GTK_CONTAINER(g_mainwindow), 5); + + g_mainvbox = gtk_vbox_new(FALSE, 0); + g_mainhbox = gtk_hbox_new(FALSE, 0); + + /* + * init routines + */ + + menu1_init(); + point_selector_init(); + view1_init(); + event_init(); + + /* + * Now that we're ready to rock 'n roll, see if we've been asked to + * press a few buttons... + */ + + while (curarg < argc) { + if (!strncmp(argv[curarg], "--cpel-input", 4)) { + curarg++; + if (curarg < argc) { + cpel_file = argv[curarg]; + curarg++; + break; + } + g_error("Missing filename after --cpel-input"); + } + if (!strncmp(argv[curarg], "--clib-input", 4)) { + curarg++; + if (curarg < argc) { + clib_file = argv[curarg]; + curarg++; + break; + } + g_error("Missing filename after --cpel-input"); + } + + if (!strncmp(argv[curarg], "--pointdefs", 3)) { + curarg++; + if (curarg < argc) { + read_event_definitions(argv[curarg]); + curarg++; + continue; + } + g_error ("Missing filename after --pointdefs\n"); + } + if (!strncmp(argv[curarg], "--event-log", 3)) { + curarg++; + if (curarg < argc) { + event_file = argv[curarg]; + curarg++; + continue; + } + g_error ("Missing filename after --event-log\n"); + } + + if (!strncmp(argv[curarg], "--ticks-per-us", 3)) { + curarg++; + if (curarg < argc) { + ticks_per_ns = 0.0; + ticks_per_ns = atof(argv[curarg]); + if (ticks_per_ns == 0.0) { + g_error("ticks-per-ns (%s) didn't convert properly\n", + argv[curarg]); + } + ticks_per_ns_set = TRUE; + curarg++; + continue; + } + g_error ("Missing filename after --event-log\n"); + } + + fprintf(stderr, + "g2 [--pointdefs <filename>] [--event-log <filename>]\n"); + fprintf(stderr, " [--ticks-per-us <value>]\n"); + fprintf(stderr, + " [--cpel-input <filename>] [--clib-input <filename]>\n"); + fprintf(stderr, + "%s\n%s\n", version_string, minor_v_string); + exit(0); + } + + if (clib_file) { + read_clib_file (clib_file); + title = clib_file; + } else if (cpel_file) { + read_cpel_file(cpel_file); + title = cpel_file; + } else if (event_file) { + read_events(event_file); + title = event_file; + } + + set_window_title(title); + + gtk_signal_connect (GTK_OBJECT (g_mainwindow), "key_press_event", + (GtkSignalFunc) view1_handle_key_press_event, NULL); + gtk_container_add(GTK_CONTAINER(g_mainvbox), g_mainhbox); + gtk_widget_show(g_mainhbox); + gtk_container_add(GTK_CONTAINER(g_mainwindow), g_mainvbox); + gtk_widget_show(g_mainvbox); + gtk_widget_show(g_mainwindow); + + gtk_main(); + return(0); +} diff --git a/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..ec394cc3 --- /dev/null +++ b/src/tools/g2/view1.c @@ -0,0 +1,3077 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2005-2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include "g2.h" +#include <time.h> +#include <string.h> +#include <vppinfra/format.h> +#include <vppinfra/elog.h> + +/* + * The main event display view. + * + * Important variables: + * + * "da" -- the drawing area, aka the screen representation of the + * event view. + * + * "pm" -- the backing pixmap for the drawing area. Note that + * all graphics operations target this backing + * store, then call gtk_widget_draw to copy a rectangle from + * the backing store onto the screen. + * + * "s_v1" -- pointer to the current v1_geometry_t object. + * + * Box heirarchy: + * s_view1_vbox + * s_view1_hbox + * da s_view1_vmenubox + * s_view1_topbutton("Top") + * s_view1_vscroll (vertical scrollbar) + * s_view1_bottombutton("Bottom") + * s_view1_hmenubox + * s_view1_startbutton("Start"); + * s_view1_hscroll(horizontal scrollbar) + * s_view1_endbutton("End") + * s_view1_zoominbutton("Zoomin") + * s_view1_searchbutton("Search") + * s_view1_searchagainbutton("Search Again") + * s_view1_zoomoutbutton("Zoomout") + * s_view1_label + */ + +/* + * Globals + */ + +GdkFont *g_font; /* a fixed-width font to use */ +GdkColor fg_black = {0, 0, 0, 0}; +GdkColor bg_white = {0, 65535, 65535, 65535}; +static boolean summary_mode = TRUE; /* start out in summary mode */ +static boolean color_mode = FALSE; /* start out in color mode */ + +/* + * Locals + */ + +/* + * user_data values passed to view1_button_click_callback, + * which is used by the various action buttons noted above + */ +enum view1_button_click { + TOP_BUTTON=1, + BOTTOM_BUTTON, + START_BUTTON, + ZOOMIN_BUTTON, + SEARCH_BUTTON, + SEARCH_AGAIN_BUTTON, + ZOOMOUT_BUTTON, + END_BUTTON, + MORE_TRACES_BUTTON, + LESS_TRACES_BUTTON, + SNAP_BUTTON, + NEXT_BUTTON, + DEL_BUTTON, + CHASE_EVENT_BUTTON, + CHASE_DATUM_BUTTON, + CHASE_TRACK_BUTTON, + UNCHASE_BUTTON, + FORWARD_BUTTON, + BACKWARD_BUTTON, + SUMMARY_BUTTON, + NOSUMMARY_BUTTON, +}; + +enum chase_mode { + CHASE_EVENT=1, + CHASE_DATUM, + CHASE_TRACK, +}; + +enum sc_dir { + SRCH_CHASE_FORWARD = 0, + SRCH_CHASE_BACKWARD = 1, +}; + +static GtkWidget *s_view1_hbox; /* see box heirarchy chart */ +static GtkWidget *s_view1_vbox; /* see box heirarchy chart */ +static GtkWidget *da; /* main drawing area */ +static GdkPixmap *pm; /* and its backing pixmap */ +static GdkCursor *norm_cursor; /* the "normal" cursor */ + +/* + * view geometry parameters + * + * Remember: + * Y increases down the page. + * Strip origin is at the top + * Payday is Friday + * Don't put your fingers in your mouth. + * + * Most of these values are in pixels + */ + +typedef struct v1_geometry { + int pid_ax_width; /* Width of the PID axis */ + int time_ax_height; /* Height of the time axis */ + int time_ax_spacing; /* TimeAxis: Space between tick-marks */ + int strip_height; /* Height of a regular PID trace */ + int pop_offset; /* Vertical offset of the detail box */ + int pid_ax_offset; /* Vertical offset of the PID axis */ + int event_offset; /* Vertical offset of the event boxes */ + int total_height; /* total height of da, see configure_event */ + int total_width; /* ditto, for width */ + + /* Derived values */ + int first_pid_index; /* Index of first displayed PID */ + int npids; /* Max number of displayed pids */ + ulonglong minvistime; /* in usec */ + ulonglong maxvistime; /* in usec */ +} v1_geometry_t; + + +/* The active geometry object */ +static v1_geometry_t s_v1record; +static v1_geometry_t *s_v1 = &s_v1record; + +/* The color array */ +static GdkColor *s_color; + +/* Snapshot ring */ +typedef struct snapshot { + struct snapshot *next; + /* Screen geometry */ + v1_geometry_t geometry; + boolean show_event[NEVENTS]; + pid_sort_t *pidvec; + /* + * Note: not worth recomputing the vertical scrollbar, just save + * its value here + */ + gfloat vscroll_value; + boolean summary_mode; + boolean color_mode; +} snapshot_t; + +static snapshot_t *s_snapshots; +static snapshot_t *s_cursnap; +static event_t *s_last_selected_event; + +/* + * various widgets, see the box heirarchy chart above + * The toolkit keeps track of these things, we could lose many of + * these pointers. + */ +static GtkWidget *s_view1_vmenubox; +static GtkWidget *s_view1_topbutton; +static GtkWidget *s_view1_bottombutton; +static GtkWidget *s_view1_more_traces_button; +static GtkWidget *s_view1_less_traces_button; + +static GtkWidget *s_view1_hmenubox; +static GtkWidget *s_view1_hmenubox2; +static GtkWidget *s_view1_startbutton; +static GtkWidget *s_view1_zoominbutton; +static GtkWidget *s_view1_searchbutton; +static GtkWidget *s_view1_srchagainbutton; +static GtkWidget *s_view1_zoomoutbutton; +static GtkWidget *s_view1_endbutton; + +static GtkWidget *s_view1_snapbutton; +static GtkWidget *s_view1_nextbutton; +static GtkWidget *s_view1_delbutton; + +static GtkWidget *s_view1_chase_event_button; +static GtkWidget *s_view1_chase_datum_button; +static GtkWidget *s_view1_chase_track_button; +static GtkWidget *s_view1_unchasebutton; + +static GtkWidget *s_view1_forward_button; +static GtkWidget *s_view1_backward_button; + +static GtkWidget *s_view1_summary_button; +static GtkWidget *s_view1_nosummary_button; + +static GtkWidget *s_view1_hscroll; +static GtkObject *s_view1_hsadj; + +static GtkWidget *s_view1_vscroll; +static GtkObject *s_view1_vsadj; + +static GtkWidget *s_view1_label; + +/* + * Search context + */ +static ulong s_srchcode; /* search event code */ +static int s_srchindex; /* last hit was at this event index */ +static boolean s_result_up; /* The SEARCH RESULT dongle is displayed */ +static boolean s_srchfail_up; /* The status line "Search Failed" is up */ +static int srch_chase_dir; /* search/chase dir, 0=>forward */ + + +/* + * Print context + */ +static int s_print_offset; /* Magic offset added to line, tbox fn codes */ +static FILE *s_printfp; + +/* + * Forward reference prototypes + */ +static void display_pid_axis(v1_geometry_t *vp); +static void display_event_data(v1_geometry_t *vp); +static void display_time_axis(v1_geometry_t *vp); +static void view1_button_click_callback(GtkButton *item, gpointer data); + +/* + * config params + */ + +gint c_view1_draw_width; +gint c_view1_draw_height; + +/* + * Zoom-In / Time Ruler cursor + */ + +#define zi_width 32 +#define zi_height 32 +#define zi_x_hot 22 +#define zi_y_hot 14 +static unsigned char zi_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x88, 0x00, + 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0xfc, 0xff, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xa0, 0x00, + 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x84, 0x00, + 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static unsigned char zi_bkgd[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x88, 0x00, + 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0xfc, 0xff, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xa0, 0x00, + 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x84, 0x00, + 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static GdkCursor *zi_cursor; +static GdkPixmap *zi_source, *zi_mask; + +/* + * Frequently-used small computations, best + * done correctly once and instantiated. + */ + +/**************************************************************************** +* dtime_per_pixel +****************************************************************************/ + +static inline double dtime_per_pixel(v1_geometry_t *vp) +{ + return ((double)(vp->maxvistime - vp->minvistime)) / + ((double)(vp->total_width - vp->pid_ax_width)); +} + +/**************************************************************************** +* message_line +* Changes the status line. Pass "" to clear the status line. +****************************************************************************/ + +void message_line (char *s) +{ + gtk_label_set_text (GTK_LABEL(s_view1_label), s); +} + +/**************************************************************************** +* set_window_title +* Changes the window title to include the specified filename. +****************************************************************************/ + +void set_window_title (const char *filename) +{ + char title[128]; + snprintf(title, sizeof(title), "g2 (%s)", filename); + gtk_window_set_title(GTK_WINDOW(g_mainwindow), title); +} + +/**************************************************************************** +* recompute_hscrollbar +* Adjust the horizontal scrollbar's adjustment object. +* +* GtkAdjustments are really cool, but have to be set up exactly +* right or the various client objects screw up completely. +* +* Note: this function is *not* called when the user clicks the scrollbar. +****************************************************************************/ + +static void recompute_hscrollbar (void) +{ + ulonglong current_width; + ulonglong event_incdec; + GtkAdjustment *adj; + event_t *ep; + + if (g_nevents == 0) + return; + + ep = (g_events + (g_nevents-1)); + current_width = s_v1->maxvistime - s_v1->minvistime; + event_incdec = (current_width) / 6; + + adj = GTK_ADJUSTMENT(s_view1_hsadj); + + /* + * Structure member decoder ring + * ----------------------------- + * lower the minimum possible value + * value the current value + * upper the maximum possible value + * step_increment end button click increment + * page_increment click in trough increment + * page_size size of currently visible area + */ + + adj->lower = (gfloat)0.00; + adj->value = (gfloat)s_v1->minvistime; + + /* Minor click: move about 1/6 of a page */ + adj->step_increment = (gfloat)event_incdec; + + /* Major click: move about 1/3 of a page. */ + adj->page_increment = (gfloat)(2*event_incdec); + + /* allow the user to go a bit past the end */ + adj->upper = adj->page_increment/3 + (gfloat)(ep->time); + adj->page_size = (gfloat)(current_width); + + /* + * Tell all clients (e.g. the visible scrollbar) to + * make themselves look right + */ + gtk_adjustment_changed(adj); + gtk_adjustment_value_changed(adj); +} + +/**************************************************************************** +* recompute_vscrollbar +* Ditto, for the vertical scrollbar +****************************************************************************/ + +static void recompute_vscrollbar (void) +{ + GtkAdjustment *adj; + + adj = GTK_ADJUSTMENT(s_view1_vsadj); + + adj->lower = (gfloat)0.00; + adj->upper = (gfloat)g_npids; + adj->value = (gfloat)0.00; + adj->step_increment = 1.00; + adj->page_increment = (gfloat)(s_v1->npids / 3); + adj->page_size = (gfloat)s_v1->npids; + gtk_adjustment_changed(adj); + gtk_adjustment_value_changed(adj); +} + +/**************************************************************************** +* format_popbox_string +****************************************************************************/ + +elog_main_t elog_main; + +void format_popbox_string (char *tmpbuf, int len, event_t *ep, event_def_t *edp) +{ + char *fp; + +#ifdef NOTDEF + sprintf(tmpbuf,"%d:", ep->code); +#endif + if (ep->flags & EVENT_FLAG_CLIB) { + elog_event_t *eep; + u8 *s; + + eep = get_clib_event (ep->datum); + + s = format (0, "%U", format_elog_event, &elog_main, eep); + memcpy (tmpbuf, s, vec_len(s)); + tmpbuf[vec_len(s)] = 0; + vec_free(s); + return; + } + + snprintf(tmpbuf, len, "%s", edp->name); + fp = edp->format; + /* Make sure there's a real format string. If so, add it */ + while (fp && *fp) { + if (*fp != ' ') { + snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf), ": "); + /* %s only supported for cpel files */ + if (fp[1] == 's') { + snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf), + edp->format, strtab_ref(ep->datum)); + } else { + snprintf(tmpbuf+strlen(tmpbuf), len - strlen(tmpbuf), + edp->format, ep->datum); + } + return; + } + fp++; + } +} + +/**************************************************************************** + * add_snapshot + ****************************************************************************/ + +static void add_snapshot(void) +{ + int i; + snapshot_t *new = g_malloc(sizeof(snapshot_t)); + + memcpy(&new->geometry, s_v1, sizeof(new->geometry)); + for (i = 0; i < NEVENTS; i++) { + new->show_event[i] = g_eventdefs[i].selected; + } + new->pidvec = g_malloc(sizeof(pid_sort_t)*g_npids); + memcpy(new->pidvec, g_pids, sizeof(pid_sort_t)*g_npids); + new->vscroll_value = GTK_ADJUSTMENT(s_view1_vsadj)->value; + new->summary_mode = summary_mode; + new->color_mode = color_mode; + + if (s_snapshots) { + new->next = s_snapshots; + s_snapshots = new; + } else { + new->next = 0; + s_snapshots = new; + } + s_cursnap = new; +} + +/**************************************************************************** + * next_snapshot + ****************************************************************************/ + +static void next_snapshot(void) +{ + snapshot_t *next; + int i; + pid_sort_t *psp; + pid_data_t *pp; + + if (!s_snapshots) { + infobox("No snapshots", "\nNo snapshots in the ring...\n"); + return; + } + + next = s_cursnap->next; + if (next == 0) + next = s_snapshots; + + s_cursnap = next; + + memcpy(s_v1, &next->geometry, sizeof(next->geometry)); + for (i = 0; i < NEVENTS; i++) { + g_eventdefs[i].selected = next->show_event[i]; + } + memcpy(g_pids, next->pidvec, sizeof(pid_sort_t)*g_npids); + color_mode = next->color_mode; + /* + * Update summary mode via a button push so that the button state is + * updated accordingly. (Should ideally clean up the view/controller + * separation properly one day.) + */ + if (summary_mode != next->summary_mode) { + view1_button_click_callback + (NULL, (gpointer)(unsigned long long) + (summary_mode ? NOSUMMARY_BUTTON : SUMMARY_BUTTON)); + } + + /* Fix the pid structure index mappings */ + psp = g_pids; + + for (i = 0; i < g_npids; i++) { + pp = psp->pid; + pp->pid_index = i; + psp++; + } + GTK_ADJUSTMENT(s_view1_vsadj)->value = next->vscroll_value; + gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj)); + recompute_hscrollbar(); + pointsel_next_snapshot(); + view1_display_when_idle(); +} + + +/**************************************************************************** + * del_snapshot + ****************************************************************************/ + +static void del_snapshot(void) +{ + snapshot_t *prev; + snapshot_t *this; + + if (!s_snapshots) { + infobox("No snapshots", "\nNo snapshots to delete...\n"); + return; + } + + prev = NULL; + this = s_snapshots; + + while (this && this != s_cursnap) { + prev = this; + this = this->next; + } + + if (this != s_cursnap) { + infobox("BUG", "\nSnapshot AWOL!\n"); + return; + } + + s_cursnap = this->next; + + /* middle of the list? */ + if (prev) { + prev->next = this->next; + g_free(this->pidvec); + g_free(this); + } else { /* start of the list */ + s_snapshots = this->next; + g_free(this->pidvec); + g_free(this); + } + + /* Note: both will be NULL after last delete */ + if (s_cursnap == NULL) + s_cursnap = s_snapshots; +} + +/**************************************************************************** + * write_snapshot + * + * VERY primitive right now - not endian or version independent, and only + * writes to "snapshots.g2" in the current directory + ****************************************************************************/ +static void write_snapshot(void) +{ + FILE *file = NULL; + snapshot_t *snap; + char *error = NULL; + int records = 0; + + if (s_snapshots == NULL) { + error = "No snapshots defined"; + errno = 0; + } + + if (!error) { + file = fopen("snapshots.g2", "w"); + if (file == NULL) { + error = "Unable to open snapshots.g2"; + } + } + + /* + * Simply serialize the arch-dependent binary data, without a care in the + * world. Don't come running to me if you try to read it and crash. + */ + for (snap = s_snapshots; !error && snap != NULL; snap = snap->next) { + if (fwrite(&snap->geometry, + sizeof(snap->geometry), 1, file) != 1 || + fwrite(&snap->show_event, + sizeof(snap->show_event), 1, file) != 1 || + fwrite(snap->pidvec, + sizeof(pid_sort_t) * g_npids, 1, file) != 1 || + fwrite(&snap->vscroll_value, + sizeof(snap->vscroll_value), 1, file) != 1 || + fwrite(&snap->summary_mode, + sizeof(snap->summary_mode), 1, file) != 1 || + fwrite(&snap->color_mode, + sizeof(snap->color_mode), 1, file) != 1) { + error = "Error writing data"; + } + records++; + } + + if (!error) { + if (fclose(file)) { + error = "Unable to close file"; + } + } + + if (error) { + infobox(error, strerror(errno)); + } else { + char buf[64]; + snprintf(buf, sizeof(buf), "Wrote %d snapshots to snapshots.g2", + records); + message_line(buf); + } +} + +/**************************************************************************** + * read_snapshot + * + * VERY primitive right now - not endian or version independent, and only reads + * from "snapshots.g2" in the current directory + ****************************************************************************/ +static void read_snapshot(void) +{ + FILE *file; + snapshot_t *snap, *next_snap; + snapshot_t *new_snaps = NULL; + char *error = NULL; + int len, i, records = 0; + pid_data_t *pp; + + file = fopen("snapshots.g2", "r"); + if (file == NULL) { + error = "Unable to open snapshots.g2"; + } + + /* + * Read in the snapshots and link them together. We insert them backwards, + * but that's tolerable. If the data is in anyway not what we expect, we'll + * probably crash. Sorry. + */ + while (!error && !feof(file)) { + snap = g_malloc(sizeof(*snap)); + snap->pidvec = NULL; /* so we can free this if there's an error */ + + len = fread(&snap->geometry, sizeof(snap->geometry), 1, file); + if (len == 0) { + /* EOF */ + g_free(snap); + break; + } else { + /* insert into list straight away */ + snap->next = new_snaps; + new_snaps = snap; + } + if (len != 1) { + error = "Problem reading first item from file"; + break; + } + if (fread(&snap->show_event, sizeof(snap->show_event), 1, file) != 1) { + error = "Problem reading second item from file"; + break; + } + len = sizeof(pid_sort_t) * g_npids; + snap->pidvec = g_malloc(len); + if (fread(snap->pidvec, len, 1, file) != 1) { + error = "Problem reading third item from file"; + break; + } + if (fread(&snap->vscroll_value, + sizeof(snap->vscroll_value), 1, file) != 1 || + fread(&snap->summary_mode, + sizeof(snap->summary_mode), 1, file) != 1 || + fread(&snap->color_mode, + sizeof(snap->color_mode), 1, file) != 1) { + error = "Problem reading final items from file"; + break; + } + + /* + * Fix up the pointers from the sorted pid vector back into our pid + * data objects, by walking the linked list of pid_data_t objects for + * every one looking for a match. This is O(n^2) grossness, but in real + * life there aren't that many pids, and it seems zippy enough. + */ + for (i = 0; i < g_npids; i++) { + for (pp = g_pid_data_list; pp != NULL; pp = pp->next) { + if (pp->pid_value == snap->pidvec[i].pid_value) { + break; + } + } + if (pp != NULL) { + snap->pidvec[i].pid = pp; + } else { + error = "Snapshot file referenced unknown pids"; + break; + } + } + + records++; + } + + if (!error) { + if (fclose(file)) { + error = "Unable to close file"; + } + } + + if (error) { + /* + * Problem - clear up any detritus + */ + infobox(error, strerror(errno)); + for (snap = new_snaps; snap != NULL; snap = next_snap) { + next_snap = snap->next; + g_free(snap); + g_free(snap->pidvec); + } + } else { + /* + * Success! trash the old snapshots and replace with the new + */ + for (snap = s_snapshots; snap != NULL; snap = next_snap) { + next_snap = snap->next; + g_free(snap->pidvec); + g_free(snap); + } + + s_cursnap = s_snapshots = new_snaps; + } + + if (error) { + infobox(error, strerror(errno)); + } else { + char buf[64]; + snprintf(buf, sizeof(buf), + "Read %d snapshots from snapshots.g2", records); + message_line(buf); + } +} + +/**************************************************************************** +* set_color +* +* Set the color for the specified pid_index, or COLOR_DEFAULT to return it +* to the usual black. +****************************************************************************/ +#define COLOR_DEFAULT (-1) +static void set_color(int pid_index) +{ + if (pid_index == COLOR_DEFAULT || !color_mode) { + gdk_gc_set_foreground(da->style->black_gc, &fg_black); + } else { + gdk_gc_set_foreground(da->style->black_gc, + &s_color[g_pids[pid_index].color_index]); + } +} + +/**************************************************************************** +* toggle_event_select +****************************************************************************/ + +static void toggle_event_select(GdkEventButton *event, v1_geometry_t *vp) +{ + int pid_index, start_index; + int x, y; + GdkRectangle *rp; + GdkRectangle hit_rect; + GdkRectangle dummy; + event_t *ep; + event_def_t *edp; + char tmpbuf [1024]; + double time_per_pixel; + + if (g_nevents == 0) + return; + + time_per_pixel = dtime_per_pixel(vp); + + start_index = find_event_index (vp->minvistime); + + /* Too far right? */ + if (start_index >= g_nevents) + return; + + /* + * To see if the mouse hit a visible event, use a variant + * of the event display loop. + */ + + hit_rect.x = (int)event->x; + hit_rect.y = (int)event->y; + hit_rect.width = 1; + hit_rect.height = 1; + + ep = (g_events + start_index); + + while ((ep->time < vp->maxvistime) && + (ep < (g_events + g_nevents))) { + pid_index = ep->pid->pid_index; + + /* First filter: pid out of range */ + if ((pid_index < vp->first_pid_index) || + (pid_index >= vp->first_pid_index + vp->npids)) { + ep++; + continue; + } + + /* Second filter: event hidden */ + edp = find_event_definition (ep->code); + if (!edp->selected) { + ep++; + continue; + } + + /* + * At this point, we know that the point is at least on the + * screen. See if the mouse hit within the bounding box + */ + + /* + * $$$$ maybe keep looping until off the edge, + * maintain a "best hit", then declare that one the winner? + */ + + pid_index -= vp->first_pid_index; + + y = pid_index*vp->strip_height + vp->event_offset; + + x = vp->pid_ax_width + + (int)(((double)(ep->time - vp->minvistime)) / time_per_pixel); + + /* Perhaps we're trying to toggle the detail box? */ + if (ep->flags & EVENT_FLAG_SELECT) { + /* Figure out the dimensions of the detail box */ + format_popbox_string(tmpbuf, sizeof(tmpbuf), ep, edp); + rp = tbox(tmpbuf, x, y - vp->pop_offset, TBOX_GETRECT_BOXED); + if (gdk_rectangle_intersect(rp, &hit_rect, &dummy)) { + ep->flags &= ~EVENT_FLAG_SELECT; + view1_display_when_idle(); + break; + } + } + + sprintf(tmpbuf, "%ld", ep->code); + + /* Figure out the dimensions of the regular box */ + rp = tbox(tmpbuf, x, y, TBOX_GETRECT_EVENT); + + if (gdk_rectangle_intersect(rp, &hit_rect, &dummy)) { + /* we hit the rectangle. */ + if (ep->flags & EVENT_FLAG_SELECT) { + ep->flags &= ~EVENT_FLAG_SELECT; + view1_display_when_idle(); + break; + } else { + set_color(ep->pid->pid_index); + + /* It wasn't selected, so put up the detail box */ + format_popbox_string(tmpbuf, sizeof(tmpbuf), ep, edp); + tbox(tmpbuf, x, y - vp->pop_offset, TBOX_DRAW_BOXED); + line(x, y-vp->pop_offset, x, y, LINE_DRAW_BLACK); + ep->flags |= EVENT_FLAG_SELECT; + ep->flags &= ~EVENT_FLAG_SEARCHRSLT; + s_last_selected_event = ep; + } + break; + } + ep++; + } +} + +/**************************************************************************** +* move_current_track +****************************************************************************/ + +typedef enum { MOVE_TOP, MOVE_BOTTOM } move_type; + +static void move_current_track(GdkEventButton *event, + v1_geometry_t *vp, + move_type type) +{ + int i; + int pid_index; + int y, delta_y; + pid_sort_t *new_pidvec; + pid_sort_t *psp; + pid_sort_t *pold, *pnew; + pid_data_t *pp; + + if (g_nevents == 0) + return; + + /* Scan pid/track axis locations, looking for a match */ + for (i = 0; i < vp->npids; i++) { + y = i*vp->strip_height + vp->pid_ax_offset; + delta_y = y - event->y; + if (delta_y < 0) + delta_y = -delta_y; + if (delta_y < 10) { + goto found; + } + + } + infobox("NOTE", "\nNo PID/Track In Range\nPlease Try Again"); + return; + + found: + pid_index = i + vp->first_pid_index; + + new_pidvec = g_malloc(sizeof(pid_sort_t)*g_npids); + pold = g_pids; + pnew = new_pidvec; + + if (type == MOVE_TOP) { + /* move to top */ + *pnew++ = g_pids[pid_index]; + for (i = 0; i < pid_index; i++) + *pnew++ = *pold++; + pold++; + i++; + for (; i < g_npids; i++) + *pnew++ = *pold++; + } else { + /* move to bottom */ + for (i = 0; i < pid_index; i++) + *pnew++ = *pold++; + pold++; + i++; + for (; i < g_npids; i++) + *pnew++ = *pold++; + *pnew = g_pids[pid_index]; + } + + g_free(g_pids); + g_pids = new_pidvec; + + /* + * Revert the pid_index mapping to an identity map, + */ + psp = g_pids; + + for (i = 0; i < g_npids; i++) { + pp = psp->pid; + pp->pid_index = i; + psp++; + } + view1_display_when_idle(); +} + +/**************************************************************************** +* zoom_event +* Process a zoom gesture. The use of doubles is required to avoid +* truncating the various variable values, which in turn would lead to +* some pretty random-looking zoom responses. +****************************************************************************/ + +void zoom_event(GdkEventButton *e1, GdkEventButton *e2, v1_geometry_t *vp) +{ + double xrange; + double time_per_pixel; + double width_in_pixels; + double center_on_time, width_in_time; + double center_on_pixel; + + /* + * Clip the zoom area to the event display area. + * Otherwise, center_on_time - width_in_time is in hyperspace + * to the left of zero + */ + + if (e1->x < vp->pid_ax_width) + e1->x = vp->pid_ax_width; + + if (e2->x < vp->pid_ax_width) + e2->x = vp->pid_ax_width; + + if (e2->x == e1->x) + goto loser_zoom_repaint; + + xrange = (double) (e2->x - e1->x); + if (xrange < 0.00) + xrange = -xrange; + + /* Actually, width in pixels of half the zoom area */ + width_in_pixels = xrange / 2.00; + time_per_pixel = dtime_per_pixel(vp); + width_in_time = width_in_pixels * time_per_pixel; + + /* Center the screen on the center of the zoom area */ + center_on_pixel = (double)((e2->x + e1->x) / 2.00) - + (double)vp->pid_ax_width; + center_on_time = center_on_pixel*time_per_pixel + (double)vp->minvistime; + + /* + * Transform back to 64-bit integer microseconds, reset the + * scrollbar, schedule a repaint. + */ + vp->minvistime = (ulonglong)(center_on_time - width_in_time); + vp->maxvistime = (ulonglong)(center_on_time + width_in_time); + +loser_zoom_repaint: + recompute_hscrollbar(); + + view1_display_when_idle(); +} + +/**************************************************************************** +* scroll_y +* +* Scroll up or down by the specified delta +* +****************************************************************************/ +static void scroll_y(int delta) +{ + int new_index = s_v1->first_pid_index + delta; + if (new_index + s_v1->npids > g_npids) + new_index = g_npids - s_v1->npids; + if (new_index < 0) + new_index = 0; + + if (new_index != s_v1->first_pid_index) { + s_v1->first_pid_index = new_index; + GTK_ADJUSTMENT(s_view1_vsadj)->value = (gdouble)new_index; + gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj)); + view1_display_when_idle(); + } +} + +/**************************************************************************** +* view1_handle_key_press_event +* Relevant definitions in: /usr/include/gtk-1.2/gdk/gdktypes.h +* +* This routine implements hotkeys for the Quake generation: +* +* W - zoom in +* S - zoom out +* A - pan left +* D - pan right +* R - pan up +* F - pan down +* T - more traces +* G - fewer traces +* +* E - toggle summary mode +* C - toggle color mode +* +* X - take snapshot +* Z - next snapshot +* P - persist snapshots to file +* L - load snapshots from file +* +* ctrl-Q - exit +* +****************************************************************************/ +gint +view1_handle_key_press_event (GtkWidget *widget, GdkEventKey *event) +{ + long long delta; + + switch (event->keyval) { + case GDK_w: // zoom in + view1_button_click_callback(NULL, (gpointer)ZOOMIN_BUTTON); + break; + + case GDK_s: // zoom out + view1_button_click_callback(NULL, (gpointer)ZOOMOUT_BUTTON); + break; + + case GDK_a: // pan left + delta = (s_v1->maxvistime - s_v1->minvistime) / 6; + if (s_v1->minvistime < delta) { + delta = s_v1->minvistime; + } + s_v1->minvistime -= delta; + s_v1->maxvistime -= delta; + recompute_hscrollbar(); + break; + + case GDK_d: // pan right + delta = (s_v1->maxvistime - s_v1->minvistime) / 6; + if (s_v1->maxvistime + delta > g_events[g_nevents - 1].time) { + /* + * @@@ this doesn't seem to quite reach the far right hand + * side correctly - not sure why. + */ + delta = g_events[g_nevents - 1].time - s_v1->maxvistime; + } + s_v1->minvistime += delta; + s_v1->maxvistime += delta; + recompute_hscrollbar(); + break; + + case GDK_r: // pan up + scroll_y(-1); + break; + + case GDK_f: // pan down + scroll_y(+1); + break; + + case GDK_t: // fewer tracks + view1_button_click_callback(NULL, (gpointer)LESS_TRACES_BUTTON); + break; + + case GDK_g: // more tracks + view1_button_click_callback(NULL, (gpointer)MORE_TRACES_BUTTON); + break; + + case GDK_e: // toggle summary mode + view1_button_click_callback + (NULL, (gpointer)(unsigned long long) + (summary_mode ? NOSUMMARY_BUTTON : SUMMARY_BUTTON)); + break; + + case GDK_c: // toggle color mode + color_mode ^= 1; + view1_display_when_idle(); + break; + + case GDK_p: // persist snapshots + write_snapshot(); + break; + + case GDK_l: // load snapshots + read_snapshot(); + break; + + case GDK_x: // take snapshot + view1_button_click_callback(NULL, (gpointer)SNAP_BUTTON); + break; + + case GDK_z: // next snapshot + view1_button_click_callback(NULL, (gpointer)NEXT_BUTTON); + break; + + case GDK_q: // ctrl-q is exit + if (event->state & GDK_CONTROL_MASK) { + gtk_main_quit(); + } + break; + } + return TRUE; +} + +/**************************************************************************** +* button_press_event +* Relevant definitions in: /usr/include/gtk-1.2/gdk/gdktypes.h +* +* This routine implements three functions: zoom-to-area, time ruler, and +* show/hide event detail popup. +* +* The left mouse button (button 1) has two simultaneous functions: event +* detail popup, and zoom-to-area. If the press and release events occur +* within a small delta-x, it's a detail popup event. Otherwise, it's +* an area zoom. +* +* The right mouse button (button 3) implements the time ruler. +****************************************************************************/ + +static gint +button_press_event (GtkWidget *widget, GdkEventButton *event) +{ + static GdkEventButton press1_event; + static boolean press1_valid; + static GdkEventButton press3_event; + static guint32 last_truler_time; + static boolean press3_valid; + static boolean zoom_bar_up; + int time_ax_y, xdelta; + char tmpbuf [128]; + double time_per_pixel; + + time_ax_y = 0; + + switch(event->type) { + case GDK_BUTTON_PRESS: + /* Capture the appropriate starting point */ + if (event->button == 1) { + press1_valid = TRUE; + press1_event = *event; + return(TRUE); + } + if (event->button == 3) { + press3_valid = TRUE; + press3_event = *event; + return(TRUE); + } + return(TRUE); + + case GDK_BUTTON_RELEASE: + /* Time ruler */ + if (press3_valid) { + press3_valid = FALSE; + /* Fix the cursor, and repaint the screen from scratch */ + gdk_window_set_cursor (da->window, norm_cursor); + view1_display_when_idle(); + return(TRUE); + } + /* Event select / zoom-to-area */ + if (press1_valid) { + press1_valid = FALSE; + xdelta = (int)(press1_event.x - event->x); + if (xdelta < 0) + xdelta = -xdelta; + + /* is the mouse more or less where it started? */ + if (xdelta < 10) { + /* Control-left-mouse => sink the track */ + /* Shift-left-mouse => raise the track */ + if ((press1_event.state & GDK_CONTROL_MASK) == + GDK_CONTROL_MASK) { + move_current_track(event, s_v1, MOVE_BOTTOM); + } else if ((press1_event.state & GDK_SHIFT_MASK) == + GDK_SHIFT_MASK) { + move_current_track(event, s_v1, MOVE_TOP); + } else { + /* No modifiers: toggle the event */ + toggle_event_select(event, s_v1); + } + /* Repaint to get rid of the zoom bar */ + if (zoom_bar_up) { + /* Fix the cursor and leave. No zoom */ + gdk_window_set_cursor (da->window, norm_cursor); + zoom_bar_up = FALSE; + break; + } + } else { /* mouse moved enough to zoom */ + zoom_event(&press1_event, event, s_v1); + gdk_window_set_cursor (da->window, norm_cursor); + zoom_bar_up = FALSE; + } + } else if (event->button == 4) { + /* scroll wheel up */ + scroll_y(event->state & GDK_SHIFT_MASK ? -10 : -1); + } else if (event->button == 5) { + /* scroll wheel down */ + scroll_y(event->state & GDK_SHIFT_MASK ? +10 : +1); + } + return(TRUE); + + case GDK_MOTION_NOTIFY: + /* Button one followed by motion: draw zoom fence and fix cursor */ + if (press1_valid) { + /* Fence, cursor already set */ + if (zoom_bar_up) + return(TRUE); + + xdelta = (int)(press1_event.x - event->x); + if (xdelta < 0) + xdelta = -xdelta; + + /* Haven't moved enough to declare a zoom sequence yet */ + if (xdelta < 10) + return(TRUE); + + /* Draw the zoom fence, use the key-down X coordinate */ + time_ax_y = s_v1->npids * s_v1->strip_height + s_v1->pid_ax_offset; + + line((int)(press1_event.x), s_v1->pop_offset, + (int)(press1_event.x), time_ax_y, LINE_DRAW_BLACK); + tbox("Zoom From Here...", (int)(press1_event.x), s_v1->pop_offset, + TBOX_DRAW_BOXED); + gdk_window_set_cursor(da->window, zi_cursor); + zoom_bar_up = TRUE; + return(TRUE); + } + if (press3_valid) { + double nsec; + + gdk_window_set_cursor(da->window, zi_cursor); + + /* + * Some filtration is needed on Solaris, or the server will hang + */ + if (event->time - last_truler_time < 75) + return(TRUE); + + last_truler_time = event->time; + + line((int)(press3_event.x), s_v1->pop_offset, + (int)(press3_event.x), time_ax_y, LINE_DRAW_BLACK); + + xdelta = (int)(press3_event.x - event->x); + if (xdelta < 0) + xdelta = -xdelta; + + time_per_pixel = ((double)(s_v1->maxvistime - s_v1->minvistime)) / + ((double)(s_v1->total_width - s_v1->pid_ax_width)); + + time_ax_y = s_v1->npids * s_v1->strip_height + s_v1->pid_ax_offset; + + line((int)(press3_event.x), s_v1->pop_offset, + (int)(press3_event.x), time_ax_y, LINE_DRAW_BLACK); + /* + * Note: use a fixed-width format so it looks like we're + * erasing and redrawing the box. + */ + nsec = ((double)xdelta)*time_per_pixel; + if (nsec >1e9) { + sprintf(tmpbuf, "%8.3f sec ", nsec/1e9); + } else if (nsec > 1e6) { + sprintf(tmpbuf, "%8.3f msec", nsec/1e6); + } else if (nsec > 1e3) { + sprintf(tmpbuf, "%8.3f usec", nsec/1e3); + } else { + sprintf(tmpbuf, "%8.0f nsec", nsec); + } + tbox(tmpbuf, (int)(press3_event.x), s_v1->pop_offset, + TBOX_DRAW_BOXED); + return(TRUE); + } + + default: + break; +#ifdef DEBUG + g_print("button:\ttype = %d\n", event->type); + g_print("\twindow = 0x%x\n", event->window); + g_print("\tsend_event = %d\n", event->send_event); + g_print("\ttime = %d\n", event->time); + g_print("\tx = %6.2f\n", event->x); + g_print("\ty = %6.2f\n", event->y); + g_print("\tpressure = %6.2f\n", event->pressure); + g_print("\txtilt = %6.2f\n", event->xtilt); + g_print("\tytilt = %6.2f\n", event->ytilt); + g_print("\tstate = %d\n", event->state); + g_print("\tbutton = %d\n", event->button); + g_print("\tsource = %d\n", event->source); + g_print("\tdeviceid = %d\n", event->deviceid); + g_print("\tx_root = %6.2f\n", event->x_root); + g_print("\ty_root = %6.2f\n", event->y_root); + return(TRUE); +#endif + } + + view1_display_when_idle(); + + return(TRUE); +} + +/**************************************************************************** +* configure_event +* Happens when the window manager resizes the viewer's main window. +****************************************************************************/ + +static gint +configure_event (GtkWidget *widget, GdkEventConfigure *event) +{ + /* Toss the previous drawing area backing store pixmap */ + if (pm) + gdk_pixmap_unref(pm); + + /* Create a new pixmap, paint it */ + pm = gdk_pixmap_new(widget->window, + widget->allocation.width, + widget->allocation.height, + -1); + gdk_draw_rectangle (pm, + widget->style->white_gc, + TRUE, + 0, 0, + widget->allocation.width, + widget->allocation.height); + + /* Reset the view geometry parameters, as required */ + s_v1->total_width = widget->allocation.width; + s_v1->total_height = widget->allocation.height; + s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) / + s_v1->strip_height; + + /* Schedule a repaint */ + view1_display_when_idle(); + return(TRUE); +} + +/**************************************************************************** +* expose_event +* Use backing store to fix the screen. +****************************************************************************/ +static gint expose_event (GtkWidget *widget, GdkEventExpose *event) +{ + gdk_draw_pixmap(widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + pm, + event->area.x, event->area.y, + event->area.x, event->area.y, + event->area.width, event->area.height); + + return(FALSE); +} + +/**************************************************************************** +* event_search_internal +* This routine searches forward from s_srchindex, looking for s_srchcode; +* wraps at the end of the buffer. +****************************************************************************/ + +boolean event_search_internal (void) +{ + event_t *ep; + int i; + int index; + int pid_index; + boolean full_redisplay = FALSE; + ulonglong current_width; + char tmpbuf [64]; + + /* No events yet? Act like the search worked, to avoid a loop */ + if (g_nevents == 0) + return(TRUE); + + ep = (g_events + s_srchindex); + ep->flags &= ~EVENT_FLAG_SEARCHRSLT; + + /* + * Assume the user wants to search [plus or minus] + * from where they are. + */ +#ifdef notdef + if (ep->time < s_v1->minvistime) + s_srchindex = find_event_index (s_v1->minvistime); +#endif + + for (i = 1; i <= g_nevents; i++) { + index = (srch_chase_dir == SRCH_CHASE_BACKWARD) ? + (s_srchindex - i) % g_nevents : + (i + s_srchindex) % g_nevents; + + ep = (g_events + index); + + if (ep->code == s_srchcode) { + if (s_srchfail_up) + message_line(""); + s_srchindex = index; + pid_index = ep->pid->pid_index; + + /* Need a vertical scroll? */ + if ((pid_index < s_v1->first_pid_index) || + (pid_index >= s_v1->first_pid_index + s_v1->npids)) { + if (pid_index > (g_npids - s_v1->npids)) + pid_index = (g_npids - s_v1->npids); + s_v1->first_pid_index = pid_index; + GTK_ADJUSTMENT(s_view1_vsadj)->value = + (gdouble)s_v1->first_pid_index; + gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj)); + full_redisplay = TRUE; + } + + /* Need a horizontal scroll? */ + if (ep->time < s_v1->minvistime || ep->time > s_v1->maxvistime) { + current_width = (s_v1->maxvistime - s_v1->minvistime); + if (ep->time < ((current_width+1) / 2)) { + s_v1->minvistime = 0ll; + s_v1->maxvistime = current_width; + } else { + s_v1->minvistime = ep->time - ((current_width+1)/2); + s_v1->maxvistime = ep->time + ((current_width+1)/2); + } + recompute_hscrollbar(); + full_redisplay = TRUE; + } + ep->flags |= EVENT_FLAG_SEARCHRSLT; + full_redisplay = TRUE; + +#ifdef NOTDEF + if (!full_redisplay){ + if (!s_result_up) { + s_result_up = TRUE; + time_per_pixel = dtime_per_pixel(s_v1); + + y = pid_index*s_v1->strip_height + s_v1->event_offset; + x = s_v1->pid_ax_width + + (int)(((double)(ep->time - s_v1->minvistime)) / + time_per_pixel); + sprintf(tmpbuf, "SEARCH RESULT"); + tbox(tmpbuf, x, y - s_v1->pop_offset, TBOX_DRAW_BOXED); + line(x, y-s_v1->pop_offset, x, y, LINE_DRAW_BLACK); + } else { + full_redisplay = TRUE; + } + } +#endif + + if (full_redisplay) + view1_display_when_idle(); + return(TRUE); + } + } + sprintf (tmpbuf, "Search for event %ld failed...\n", s_srchcode); + message_line(tmpbuf); + s_srchfail_up = TRUE; + return(TRUE); +} + +/**************************************************************************** +* event_search_callback +****************************************************************************/ + +boolean event_search_callback (char *s) +{ + /* No events yet? Act like the search worked, to avoid a loop */ + if (g_nevents == 0) + return(TRUE); + + s_srchcode = atol(s); + + if (s_srchcode == 0) + return(FALSE); + + return(event_search_internal()); +} + +/**************************************************************************** +* event_search +****************************************************************************/ + +static void event_search (void) +{ + modal_dialog ("Event Search: Please Enter Event Code", + "Invalid: Please Reenter Event Code", NULL, + event_search_callback); +} + +/**************************************************************************** +* init_track_colors +****************************************************************************/ +static void init_track_colors(void) +{ + int i; + unsigned hash; + char *label_char; + unsigned RGB[3]; + gboolean dont_care[g_npids]; + + /* + * If we've already allocated the colors once, then in theory we should + * just be able to re-order the GCs already created to match the new track + * order; the track -> color mapping doesn't currently change at runtime. + * However, it's easier just to allocate everything from fresh. As a nod in + * the direction of politeness towards our poor abused X server, we at + * least mop up the previously allocated GCs first, although in practice + * even omitting this didn't seem to cause a problem. + */ + if (s_color != NULL ) { + gdk_colormap_free_colors(gtk_widget_get_colormap(da), + s_color, g_npids); + memset(s_color, 0, sizeof(GdkColor) * g_npids); + } else { + /* + * First time through: allocate the array to hold the GCs. + */ + s_color = g_malloc(sizeof(GdkColor) * g_npids); + } + + /* + * Go through and assign a color for each track. + */ + for (i = 0; i < g_npids; i++) { + /* + * We compute the color from a hash of the thread name. That way we get + * a distribution of different colors, and the same thread has the same + * color across multiple data sets. Unfortunately, even though the + * process name and thread id are invariant across data sets, the + * process id isn't, so we want to exclude that from the hash. Since + * the pid appears in parentheses after the process name and tid, we + * can just stop at the '(' character. + * + * We could create a substring and use the CLIB Jenkins hash, but given + * we're hashing ascii data, a suitable Bernstein hash is pretty much + * just as good, and it's easiest just to compute it inline. + */ + label_char = get_track_label(g_pids[i].pid_value); + hash = 0; + while (*label_char != '\0' && *label_char != '(') { + hash = hash * 33 + *label_char++; + } + hash += hash >> 5; /* even out the lower order bits a touch */ + + /* + * OK, now we have our hash. We get the color by using the first three + * bytes of the hash for the RGB values (expanded from 8 to 16 bits), + * and then use the fourth byte to choose one of R, G, B and mask this + * one down. This ensures the color can't be too close to white and + * therefore hard to see. + * + * We also drop the top bit of the green, since bright green on its own + * is hard to see against white. Generally we err on the side of + * keeping it dark, rather than using the full spectrum of colors. This + * does result in something of a preponderance of muddy colors and a + * bit of a lack of cheery bright ones, but at least you can read + * everything. It would be nice to do better. + */ + RGB[0] = (hash & 0xff000000) >> 16; + RGB[1] = (hash & 0x007f0000) >> 8; + RGB[2] = (hash & 0x0000ff00); + RGB[hash % 3] &= 0x1fff; + + { + GdkColor color = {0, RGB[0], RGB[1], RGB[2]}; + s_color[i] = color; + g_pids[i].color_index = i; + } + } + + /* + * Actually allocate the colors in one bulk operation. We ignore the return + * values. + */ + gdk_colormap_alloc_colors(gtk_widget_get_colormap(da), + s_color, g_npids, FALSE, TRUE, dont_care); +} + + +/**************************************************************************** +* chase_event_etc +* Reorder the pid_index fields so the viewer "chases" the last selected +* event. +****************************************************************************/ + +static void chase_event_etc(enum chase_mode mode) +{ + pid_sort_t *psp, *new_pidvec; + pid_data_t *pp; + event_t *ep; + int pids_mapped; + ulong code_to_chase; + ulong datum_to_chase; + ulong pid_to_chase; + int i; + int winner; + + if (!s_last_selected_event) { + infobox("No selected event", + "\nPlease select an event and try again...\n"); + return; + } + + /* Clear all index assignments */ + psp = g_pids; + for (i = 0; i < g_npids; i++) { + pp = psp->pid; + pp->pid_index = 0xFFFFFFFF; + psp++; + } + + ep = s_last_selected_event; + code_to_chase = ep->code; + datum_to_chase = ep->datum; + pid_to_chase = ep->pid->pid_value; + pids_mapped = 0; + new_pidvec = g_malloc(sizeof(pid_sort_t)*g_npids); + + while (1) { + if (srch_chase_dir == SRCH_CHASE_FORWARD) { + if (ep >= g_events + g_nevents) + break; + } else { + if (ep < g_events) + break; + } + + winner = 0; + switch(mode) { + case CHASE_EVENT: + if (ep->code == code_to_chase) { + winner = 1; + } + break; + + case CHASE_DATUM: + if (ep->datum == datum_to_chase) { + winner = 1; + } + break; + + case CHASE_TRACK: + if (ep->pid->pid_value == pid_to_chase) { + winner = 1; + } + break; + + default: + infobox("BUG", "unknown mode in chase_event_etc\n"); + break; + } + + if (winner) { + if (ep->pid->pid_index == 0xFFFFFFFF) { + ep->pid->pid_index = pids_mapped; + new_pidvec[pids_mapped].pid = ep->pid; + new_pidvec[pids_mapped].pid_value = ep->pid->pid_value; + new_pidvec[pids_mapped].color_index = 0; + pids_mapped++; + if (pids_mapped == g_npids) + break; + } + } + if (srch_chase_dir == SRCH_CHASE_FORWARD) + ep++; + else + ep--; + } + + /* Pass 2, first-to-last, to collect stragglers */ + ep = g_events; + + while (ep < g_events + g_nevents) { + if (ep->pid->pid_index == 0xFFFFFFFF) { + ep->pid->pid_index = pids_mapped; + new_pidvec[pids_mapped].pid = ep->pid; + new_pidvec[pids_mapped].pid_value = ep->pid->pid_value; + new_pidvec[pids_mapped].color_index = 0; + pids_mapped++; + if (pids_mapped == g_npids) + break; + } + ep++; + } + + if (pids_mapped != g_npids) { + infobox("BUG", "\nDidn't map all pids in chase_event_etc\n"); + } + + g_free (g_pids); + g_pids = new_pidvec; + + /* + * The new g_pids vector contains the "chase" sort, so we revert + * the pid_index mapping to an identity map + */ + psp = g_pids; + + for (i = 0; i < g_npids; i++) { + pp = psp->pid; + pp->pid_index = i; + psp++; + } + + /* AutoScroll the PID axis so we show the first "chased" event */ + s_v1->first_pid_index = 0; + GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00; + gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj)); + init_track_colors(); + view1_display_when_idle(); +} + +/**************************************************************************** +* unchase_event_etc +* Copy g_original_pids to g_pids, revert index mapping +****************************************************************************/ +static void unchase_event_etc(void) +{ + int i; + pid_sort_t *psp; + pid_data_t *pp; + + memcpy (g_pids, g_original_pids, sizeof(pid_sort_t)*g_npids); + + /* Fix the pid structure index mappings */ + psp = g_pids; + + for (i = 0; i < g_npids; i++) { + pp = psp->pid; + pp->pid_index = i; + psp++; + } + + /* Scroll PID axis to the top */ + s_v1->first_pid_index = 0; + GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00; + gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj)); + init_track_colors(); + view1_display_when_idle(); +} + +/**************************************************************************** +* print_ps_header +* To fit a reasonable-sized landscape mode plot onto letter-size paper, +* scale everything by .75. +****************************************************************************/ + +static void print_ps_header (v1_geometry_t *vp, char *filename) +{ + time_t now; + + now = time(0); + + fprintf(s_printfp, "%%%%!PS-Adobe-3.0 EPSF-3.0\n"); + fprintf(s_printfp, "%%%%Creator: G2 Event Viewer\n"); + fprintf(s_printfp, "%%%%Title: %s\n", filename); + fprintf(s_printfp, "%%%%CreationDate: %s", ctime(&now)); + fprintf(s_printfp, "%%%%DocumentData: Clean7Bit\n"); + fprintf(s_printfp, "%%%%Origin: 0 0\n"); + fprintf(s_printfp, "%%%%BoundingBox: 0 0 %d %d\n", vp->total_height, + vp->total_width); + fprintf(s_printfp, "%%%%LanguageLevel: 2\n"); + fprintf(s_printfp, "%%%%Pages: 1\n"); + fprintf(s_printfp, "%%%%Page: 1 1\n"); + fprintf(s_printfp, "%%%%EOF\n"); + fprintf(s_printfp, "/Times-Roman findfont\n"); + fprintf(s_printfp, "12 scalefont\n"); + fprintf(s_printfp, "setfont\n"); + fprintf(s_printfp, ".75 .75 scale\n"); +} + +/**************************************************************************** +* xrt +* Xcoordinate rotate and translate. We need to emit postscript that +* has a reasonable aspect ratio for printing. To do that, we rotate the +* intended picture by 90 degrees, using the standard 2D rotation +* formula: +* +* Xr = x*cos(theta) - y*sin(theta); +* Yr = x*sin(theta) + y*cos(theta); +* +* If we let theta = 90, this reduces to +* Xr = -y +* Yr = x +* +* Translate back to the origin in X by adding Ymax, yielding +* Xrt = Ymax - y +****************************************************************************/ + +static inline int xrt(int x, int y) +{ + return (s_v1->total_height - y); +} + +static inline int yrt(int x, int y) +{ + return(x); +} + +/**************************************************************************** +* print_screen_callback +****************************************************************************/ + +static boolean print_screen_callback(char *filename) +{ + s_printfp = fopen (filename, "wt"); + + if (s_printfp == NULL) + return(FALSE); + + /* + * This variable allows us to magically turn the view1 display + * code into a print-driver, with a minimum of fuss. The idea is to + * magically change TBOX_DRAW_XXX into TBOX_PRINT_XXX by adding + * the required value, aka s_print_offset. + * Make sure to fix g2.h if you mess here, or vice versa. + */ + s_print_offset = TBOX_PRINT_PLAIN - TBOX_DRAW_PLAIN; + + print_ps_header(s_v1, filename); + + display_pid_axis(s_v1); + display_event_data(s_v1); + display_time_axis(s_v1); + + fclose (s_printfp); + s_printfp = 0; + s_print_offset = 0; + + /* For tactile feedback */ + view1_display_when_idle(); + return(TRUE); +} + +/**************************************************************************** +* view1_button_click_callback +****************************************************************************/ + +static void view1_button_click_callback(GtkButton *item, gpointer data) +{ + enum view1_button_click click = (enum view1_button_click) data; + event_t *ep; + ulonglong event_incdec; + ulonglong current_width; + ulonglong zoom_delta; + + + current_width = s_v1->maxvistime - s_v1->minvistime; + event_incdec = (current_width) / 3; + + if (event_incdec == 0LL) + event_incdec = 1; + + zoom_delta = (s_v1->maxvistime - s_v1->minvistime) / 6; + + switch(click) { + case TOP_BUTTON: + /* First PID to top of window */ + s_v1->first_pid_index = 0; + GTK_ADJUSTMENT(s_view1_vsadj)->value = 0.00; + gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj)); + break; + + case BOTTOM_BUTTON: + s_v1->first_pid_index = g_npids - s_v1->npids; + if (s_v1->first_pid_index < 0) + s_v1->first_pid_index = 0; + GTK_ADJUSTMENT(s_view1_vsadj)->value = (gdouble)s_v1->first_pid_index; + gtk_adjustment_value_changed(GTK_ADJUSTMENT(s_view1_vsadj)); + break; + + case SNAP_BUTTON: + add_snapshot(); + break; + + case NEXT_BUTTON: + next_snapshot(); + break; + + case DEL_BUTTON: + del_snapshot(); + break; + + case CHASE_EVENT_BUTTON: + chase_event_etc(CHASE_EVENT); + break; + + case CHASE_DATUM_BUTTON: + chase_event_etc(CHASE_DATUM); + break; + + case CHASE_TRACK_BUTTON: + chase_event_etc(CHASE_TRACK); + break; + + case UNCHASE_BUTTON: + unchase_event_etc(); + break; + + case START_BUTTON: + start_button: + s_v1->minvistime = 0LL; + s_v1->maxvistime = current_width; + recompute_hscrollbar(); + break; + + case ZOOMIN_BUTTON: + s_v1->minvistime += zoom_delta; + s_v1->maxvistime -= zoom_delta; + recompute_hscrollbar(); + break; + + case SEARCH_AGAIN_BUTTON: + if (s_srchcode) { + event_search_internal(); + break; + } + /* NOTE FALLTHROUGH */ + + case SEARCH_BUTTON: + event_search(); + break; + + case ZOOMOUT_BUTTON: + if (zoom_delta == 0LL) + zoom_delta = 1; + + if (s_v1->minvistime >= zoom_delta) { + s_v1->minvistime -= zoom_delta; + s_v1->maxvistime += zoom_delta; + } else { + s_v1->minvistime = 0; + s_v1->maxvistime += zoom_delta*2; + } + + if ((s_v1->maxvistime - s_v1->minvistime) * 8 > + g_events[g_nevents-1].time * 9) { + s_v1->minvistime = 0; + s_v1->maxvistime = g_events[g_nevents-1].time * 9 / 8; + } + recompute_hscrollbar(); + break; + + case END_BUTTON: + ep = (g_events + g_nevents - 1); + s_v1->maxvistime = ep->time + event_incdec/3; + s_v1->minvistime = s_v1->maxvistime - current_width; + if (s_v1->minvistime > s_v1->maxvistime) + goto start_button; + recompute_hscrollbar(); + break; + + case MORE_TRACES_BUTTON: + /* Reduce the strip height to fit more traces on screen */ + s_v1->strip_height -= 1; + + if (s_v1->strip_height < 1) { + s_v1->strip_height = 1; + } + + /* Recalculate the number of strips on the screen */ + s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) / + s_v1->strip_height; + recompute_vscrollbar(); + break; + + case LESS_TRACES_BUTTON: + /* Increase the strip height to fit fewer on the screen */ + s_v1->strip_height += 1; + if (s_v1->strip_height > 80) { + s_v1->strip_height = 80; + } + + /* Recalculate the number of strips on the screen */ + s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) / + s_v1->strip_height; + recompute_vscrollbar(); + break; + + case FORWARD_BUTTON: + srch_chase_dir = SRCH_CHASE_FORWARD; + gtk_widget_hide (s_view1_forward_button); + gtk_widget_show (s_view1_backward_button); + break; + + case BACKWARD_BUTTON: + srch_chase_dir = SRCH_CHASE_BACKWARD; + gtk_widget_show (s_view1_forward_button); + gtk_widget_hide (s_view1_backward_button); + break; + + case SUMMARY_BUTTON: + summary_mode = TRUE; + gtk_widget_hide (s_view1_summary_button); + gtk_widget_show (s_view1_nosummary_button); + break; + + case NOSUMMARY_BUTTON: + summary_mode = FALSE; + gtk_widget_show (s_view1_summary_button); + gtk_widget_hide (s_view1_nosummary_button); + break; + } + + view1_display_when_idle(); +} + +/**************************************************************************** +* view1_print_callback +****************************************************************************/ + +void view1_print_callback (GtkToggleButton *notused, gpointer nu2) +{ + modal_dialog("Print Screen (PostScript format) to file:", + "Invalid file: Print Screen to file:", + "g2.ps", print_screen_callback); +} + +/**************************************************************************** +* view1_hscroll +****************************************************************************/ + +static void view1_hscroll (GtkAdjustment *adj, GtkWidget *notused) +{ + ulonglong current_width; + + current_width = (s_v1->maxvistime - s_v1->minvistime); + + s_v1->minvistime = (ulonglong)(adj->value); + s_v1->maxvistime = s_v1->minvistime + current_width; + + view1_display_when_idle(); + +#ifdef NOTDEF + g_print ("adj->lower = %.2f\n", adj->lower); + g_print ("adj->upper = %.2f\n", adj->upper); + g_print ("adj->value = %.2f\n", adj->value); + g_print ("adj->step_increment = %.2f\n", adj->step_increment); + g_print ("adj->page_increment = %.2f\n", adj->page_increment); + g_print ("adj->page_size = %.2f\n", adj->page_size); +#endif +} + +/**************************************************************************** +* view1_vscroll +****************************************************************************/ + +static void view1_vscroll (GtkAdjustment *adj, GtkWidget *notused) +{ + s_v1->first_pid_index = (int)adj->value; + view1_display_when_idle(); +} + +void set_pid_ax_width(int width) +{ + s_v1->pid_ax_width = width; + view1_display_when_idle(); +} + +/**************************************************************************** +* view1_init +****************************************************************************/ + +void view1_init(void) +{ + + c_view1_draw_width = atol(getprop_default("drawbox_width", "700")); + c_view1_draw_height = atol(getprop_default("drawbox_height", "400")); + + s_v1->pid_ax_width = 80; + s_v1->time_ax_height = 80; + s_v1->time_ax_spacing = 100; + s_v1->strip_height = 25; + s_v1->pop_offset = 20; + s_v1->pid_ax_offset = 34; + s_v1->event_offset = 40; + s_v1->total_height = c_view1_draw_height; + s_v1->total_width = c_view1_draw_width; + s_v1->first_pid_index = 0; + + s_v1->npids = (s_v1->total_height - s_v1->time_ax_height) / + s_v1->strip_height; + + s_v1->minvistime = 0; + s_v1->maxvistime = 200; + + s_view1_vbox = gtk_vbox_new(FALSE, 5); + + s_view1_hbox = gtk_hbox_new(FALSE, 5); + + da = gtk_drawing_area_new(); + gtk_drawing_area_size(GTK_DRAWING_AREA(da), c_view1_draw_width, + c_view1_draw_height); + +#ifdef NOTDEF + gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event", + (GtkSignalFunc) motion_notify_event, NULL); +#endif + + gtk_signal_connect (GTK_OBJECT (da), "expose_event", + (GtkSignalFunc) expose_event, NULL); + + gtk_signal_connect (GTK_OBJECT(da),"configure_event", + (GtkSignalFunc) configure_event, NULL); + + gtk_signal_connect (GTK_OBJECT (da), "button_press_event", + (GtkSignalFunc) button_press_event, NULL); + + gtk_signal_connect (GTK_OBJECT (da), "button_release_event", + (GtkSignalFunc) button_press_event, NULL); + + gtk_signal_connect (GTK_OBJECT (da), "motion_notify_event", + (GtkSignalFunc) button_press_event, NULL); + + gtk_widget_set_events (da, GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK + | GDK_BUTTON_MOTION_MASK); + + + gtk_box_pack_start(GTK_BOX(s_view1_hbox), da, TRUE, TRUE, 0); + + g_font = gdk_font_load ("8x13"); + if (g_font == NULL) { + g_error("Couldn't load 8x13 font...\n"); + } + gdk_font_ref(g_font); + + /* PID axis menu */ + s_view1_vmenubox = gtk_vbox_new(FALSE, 5); + + s_view1_vsadj = gtk_adjustment_new(0.0 /* initial value */, + 0.0 /* minimum value */, + 2000.0 /* maximum value */, + 0.1 /* step increment */, + 10.0/* page increment */, + 10.0/* page size */); + + s_view1_vscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT(s_view1_vsadj)); + + gtk_signal_connect (GTK_OBJECT (s_view1_vsadj), "value-changed", + GTK_SIGNAL_FUNC (view1_vscroll), + (gpointer)s_view1_vscroll); + + s_view1_topbutton = gtk_button_new_with_label("Top"); + s_view1_bottombutton = gtk_button_new_with_label("Bottom"); + + gtk_signal_connect (GTK_OBJECT(s_view1_topbutton), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) TOP_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_bottombutton), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) BOTTOM_BUTTON); + + /* More Traces button and Less Traces button */ + s_view1_more_traces_button = gtk_button_new_with_label("More Traces"); + s_view1_less_traces_button = gtk_button_new_with_label("Less Traces"); + gtk_signal_connect (GTK_OBJECT(s_view1_more_traces_button), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) MORE_TRACES_BUTTON); + gtk_signal_connect (GTK_OBJECT(s_view1_less_traces_button), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) LESS_TRACES_BUTTON); + +#ifdef NOTDEF + /* Trick to bottom-justify the menu: */ + s_view1_pad1 = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_pad1, + TRUE, FALSE, 0); + +#endif + + gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_topbutton, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_vscroll, + TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_bottombutton, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_more_traces_button, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_vmenubox), s_view1_less_traces_button, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hbox), s_view1_vmenubox, + FALSE, FALSE, 0); + + /* Time axis menu */ + + s_view1_hmenubox = gtk_hbox_new(FALSE, 5); + + s_view1_startbutton = gtk_button_new_with_label("Start"); + + s_view1_zoominbutton = gtk_button_new_with_label("ZoomIn"); + + s_view1_searchbutton = gtk_button_new_with_label("Search"); + + s_view1_srchagainbutton = gtk_button_new_with_label("Search Again"); + + s_view1_zoomoutbutton = gtk_button_new_with_label("ZoomOut"); + + s_view1_endbutton = gtk_button_new_with_label("End"); + + gtk_signal_connect (GTK_OBJECT(s_view1_startbutton), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) START_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_zoominbutton), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) ZOOMIN_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_searchbutton), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) SEARCH_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_srchagainbutton), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) SEARCH_AGAIN_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_zoomoutbutton), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) ZOOMOUT_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_endbutton), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) END_BUTTON); + + s_view1_hsadj = gtk_adjustment_new(0.0 /* initial value */, + 0.0 /* minimum value */, + 2000.0 /* maximum value */, + 0.1 /* step increment */, + 10.0/* page increment */, + 10.0/* page size */); + + s_view1_hscroll = gtk_hscrollbar_new (GTK_ADJUSTMENT(s_view1_hsadj)); + + gtk_signal_connect (GTK_OBJECT (s_view1_hsadj), "value-changed", + GTK_SIGNAL_FUNC (view1_hscroll), + (gpointer)s_view1_hscroll); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_startbutton, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_hscroll, + TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_endbutton, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_zoominbutton, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_searchbutton, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_srchagainbutton, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox), s_view1_zoomoutbutton, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hbox, + TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hmenubox, + FALSE, FALSE, 0); + + + s_view1_hmenubox2 = gtk_hbox_new(FALSE, 5); + + s_view1_snapbutton = gtk_button_new_with_label("Snap"); + + s_view1_nextbutton = gtk_button_new_with_label("Next"); + + s_view1_delbutton = gtk_button_new_with_label("Del"); + + s_view1_chase_event_button = gtk_button_new_with_label("ChaseEvent"); + + s_view1_chase_datum_button = gtk_button_new_with_label("ChaseDatum"); + + s_view1_chase_track_button = gtk_button_new_with_label("ChaseTrack"); + + s_view1_unchasebutton = gtk_button_new_with_label("NoChase"); + + s_view1_forward_button = gtk_button_new_with_label("->SrchChase(is<-)"); + s_view1_backward_button = gtk_button_new_with_label("<-SrchChase(is->)"); + + s_view1_summary_button = gtk_button_new_with_label("Summary"); + s_view1_nosummary_button = gtk_button_new_with_label("NoSummary"); + + gtk_signal_connect (GTK_OBJECT(s_view1_snapbutton), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) SNAP_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_nextbutton), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) NEXT_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_delbutton), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) DEL_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_chase_event_button), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) CHASE_EVENT_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_chase_datum_button), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) CHASE_DATUM_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_chase_track_button), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) CHASE_TRACK_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_unchasebutton), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) UNCHASE_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_forward_button), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) FORWARD_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_backward_button), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) BACKWARD_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_summary_button), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) SUMMARY_BUTTON); + + gtk_signal_connect (GTK_OBJECT(s_view1_nosummary_button), "clicked", + GTK_SIGNAL_FUNC(view1_button_click_callback), + (gpointer) NOSUMMARY_BUTTON); + + gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_hmenubox2, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_snapbutton, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_nextbutton, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_delbutton, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_event_button, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_datum_button, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_chase_track_button, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_unchasebutton, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_forward_button, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_backward_button, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_summary_button, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(s_view1_hmenubox2), s_view1_nosummary_button, + FALSE, FALSE, 0); + + s_view1_label = gtk_label_new(NULL); + + gtk_box_pack_start (GTK_BOX(s_view1_vbox), s_view1_label, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX(g_mainhbox), s_view1_vbox, + TRUE, TRUE, 0); + + gtk_widget_show_all (s_view1_vbox); + GTK_WIDGET_SET_FLAGS(da, GTK_CAN_FOCUS); + gtk_widget_grab_focus(da); + + gtk_widget_hide (s_view1_forward_button); + gtk_widget_hide (summary_mode ? s_view1_summary_button + : s_view1_nosummary_button); + + zi_source = gdk_bitmap_create_from_data (NULL, (char *)zi_bits, zi_width, + zi_height); + zi_mask = gdk_bitmap_create_from_data (NULL, (char *)zi_bkgd, zi_width, + zi_height); + + zi_cursor = (GdkCursor *) gdk_cursor_new_from_pixmap (zi_source, + zi_mask, &fg_black, + &bg_white, zi_x_hot, + zi_y_hot); + gdk_pixmap_unref (zi_source); + gdk_pixmap_unref (zi_mask); + + norm_cursor = (GdkCursor *) gdk_cursor_new (GDK_TOP_LEFT_ARROW); +} + +/**************************************************************************** +* line_print +****************************************************************************/ + +void line_print (int x1, int y1, int x2, int y2) +{ + fprintf(s_printfp, "newpath\n"); + fprintf(s_printfp, "%d %d moveto\n", xrt(x1, s_v1->total_height - y1), + yrt(x1, s_v1->total_height - y1)); + + fprintf(s_printfp, "%d %d lineto\n", xrt (x2, s_v1->total_height - y2), + yrt (x2, s_v1->total_height - y2)); + fprintf(s_printfp, "1 setlinewidth\n"); + fprintf(s_printfp, "stroke\n"); +} + +/**************************************************************************** +* tbox_print +****************************************************************************/ +GdkRectangle *tbox_print (char *s, int x, int y, enum view1_tbox_fn function, + GdkRectangle *rp) +{ + if (function == TBOX_PRINT_BOXED) { + rp->width -= 4; + } + + if ((function == TBOX_PRINT_BOXED) || + (function == TBOX_PRINT_EVENT)) { + + fprintf(s_printfp, "newpath\n"); + fprintf(s_printfp, "0 setlinewidth\n"); + fprintf(s_printfp, "%d %d moveto\n", + xrt(rp->x, s_v1->total_height - rp->y), + yrt(rp->x, s_v1->total_height - rp->y)); + + fprintf(s_printfp, "%d %d lineto\n", + xrt (rp->x+rp->width, s_v1->total_height - rp->y), + yrt (rp->x+rp->width, s_v1->total_height - rp->y)); + + fprintf(s_printfp, "%d %d lineto\n", + xrt(rp->x+rp->width, s_v1->total_height - (rp->y+rp->height)), + yrt(rp->x+rp->width, s_v1->total_height - (rp->y+rp->height))); + + fprintf(s_printfp, "%d %d lineto\n", + xrt(rp->x, s_v1->total_height - (rp->y+rp->height)), + yrt(rp->x, s_v1->total_height - (rp->y+rp->height))); + + fprintf(s_printfp, "%d %d lineto\n", + xrt(rp->x, s_v1->total_height - rp->y), + yrt(rp->x, s_v1->total_height - rp->y)); + + fprintf(s_printfp, "stroke\n"); + } + + if ((function == TBOX_PRINT_BOXED) || + (function == TBOX_PRINT_PLAIN)) { + + fprintf(s_printfp, "newpath\n"); + fprintf(s_printfp, "%d %d moveto\n", + xrt(x, s_v1->total_height - (y-2)), + yrt(x, s_v1->total_height - (y-2))); + fprintf(s_printfp, "gsave\n"); + fprintf(s_printfp, "90 rotate\n"); + fprintf(s_printfp, "(%s) show\n", s); + fprintf(s_printfp, "grestore\n"); + } + + return(rp); +} + +/**************************************************************************** +* tbox - draws an optionally boxed string whose lower lefthand +* corner is at (x, y). As usual, Y is backwards. +****************************************************************************/ + +GdkRectangle *tbox (char *s, int x, int y, enum view1_tbox_fn function) +{ + static GdkRectangle update_rect; + gint lbearing, rbearing, width, ascent, descent; + + gdk_string_extents (g_font, s, + &lbearing, &rbearing, + &width, &ascent, &descent); + + /* + * If we have enough room to display full size events, then just + * use the BOXED function instead of the EVENT function. + */ + if (s_v1->strip_height > 9) { + switch (function) { + case TBOX_DRAW_EVENT: function = TBOX_DRAW_BOXED; break; + case TBOX_GETRECT_EVENT: function = TBOX_GETRECT_BOXED; break; + case TBOX_PRINT_EVENT: function = TBOX_PRINT_BOXED; break; + default: + break; + /* Nothing */ + } + } + + switch (function) { + case TBOX_DRAW_BOXED: + gdk_draw_rectangle (pm, da->style->white_gc, TRUE, + x, y - (ascent+descent+3), width + 2, + ascent + descent + 3); + + gdk_draw_rectangle (pm, da->style->black_gc, FALSE, + x, y - (ascent+descent+3), width + 2, + ascent + descent + 3); + + gdk_draw_string (pm, g_font, da->style->black_gc, + x + 1, y - 1, (const gchar *)s); + /* NOTE FALLTHROUGH */ + case TBOX_GETRECT_BOXED: + update_rect.x = x; + update_rect.y = y -(ascent+descent+3); + update_rect.width = width + 3; + update_rect.height = ascent + descent + 4; + if (function == TBOX_DRAW_BOXED) + gtk_widget_draw (da, &update_rect); + break; + + case TBOX_DRAW_EVENT: + /* We have a small event to draw...no text */ + gdk_draw_rectangle (pm, da->style->black_gc, FALSE, + x, y - 1, 3, 3); + /* NOTE FALLTHROUGH */ + case TBOX_GETRECT_EVENT: + update_rect.x = x; + update_rect.y = y - 1; + update_rect.width = 4; + update_rect.height = 4; + if (function == TBOX_DRAW_EVENT) + gtk_widget_draw (da, &update_rect); + break; + + + case TBOX_DRAW_PLAIN: + + gdk_draw_string (pm, g_font, da->style->black_gc, + x + 1, y - 1, (const gchar *)s); + /* NOTE FALLTHROUGH */ + case TBOX_GETRECT_PLAIN: + update_rect.x = x; + update_rect.y = y -(ascent+descent+1); + update_rect.width = width; + update_rect.height = ascent + descent; + if (function == TBOX_DRAW_PLAIN) + gtk_widget_draw (da, &update_rect); + break; + + case TBOX_PRINT_BOXED: + update_rect.x = x; + update_rect.y = y -(ascent+descent+3); + update_rect.width = width + 3; + update_rect.height = ascent + descent + 4; + /* note fallthrough */ + case TBOX_PRINT_PLAIN: + return(tbox_print(s, x, y, function, &update_rect)); + + case TBOX_PRINT_EVENT: + /* We have a small event box to print...no text */ + update_rect.x = x; + update_rect.y = y - 1; + update_rect.width = 4; + update_rect.height = 4; + return(tbox_print(s, x, y, function, &update_rect)); + } + return(&update_rect); +} + +/**************************************************************************** +* line +* +* For lines there is a primitive batching facility, that doesn't update +* the drawing area until the batch is complete. This is handy for drawing +* the pid axis and for summary mode. +* +* line_batch_mode contains the state for this: +* +* BATCH_OFF: no batching, update for every line +* BATCH_NEW: just entered a batch, so initialize the area to update from +* scratch +* BATCH_EXISTING: have drawn at least one line in batch mode, so the update +* area should only be expanded from now on to include the +* union of the "rectangular hull" of all lines +****************************************************************************/ + +static enum { BATCH_OFF, BATCH_NEW, BATCH_EXISTING } line_batch_mode; +static int line_batch_count; +static int line_minx, line_miny, line_maxx, line_maxy; + +void line_batch_start (void) +{ + line_batch_mode = BATCH_NEW; + line_batch_count = 0; +} + +void line_batch_end (void) +{ + GdkRectangle update_rect; + if (line_batch_count > 0) { + update_rect.x = line_minx; + update_rect.y = line_miny; + update_rect.width = (line_maxx - line_minx) + 1; + update_rect.height = (line_maxy - line_miny) + 1; + gtk_widget_draw (da, &update_rect); + } + line_batch_mode = BATCH_OFF; +} + +void line (int x1, int y1, int x2, int y2, enum view1_line_fn function) +{ + GdkRectangle update_rect; + GdkGC *gc = NULL; + + switch(function) { + case LINE_DRAW_BLACK: + gc = da->style->black_gc; + break; + + case LINE_DRAW_WHITE: + gc = da->style->white_gc; + break; + + case LINE_PRINT: + line_print (x1, y1, x2, y2); + return; + } + + gdk_draw_line (pm, gc, x1, y1, x2, y2); + + switch (line_batch_mode) { + case BATCH_OFF: + update_rect.x = x1; + update_rect.y = y1; + update_rect.width = (x2-x1) + 1; + update_rect.height = (y2-y1) + 1; + gtk_widget_draw (da, &update_rect); + break; + + case BATCH_NEW: + line_minx = x1; + line_maxx = x2; + line_miny = y1; + line_maxy = y2; + line_batch_mode = BATCH_EXISTING; + line_batch_count = 1; + break; + + case BATCH_EXISTING: + if (line_minx > x1) + line_minx = x1; + if (line_miny > y1) + line_miny = y1; + if (line_maxx < x2) + line_maxx = x2; + if (line_maxy < y2) + line_maxy = y2; + line_batch_count++; + break; + } +} + + +/**************************************************************************** +* display_pid_axis +****************************************************************************/ + +static void display_pid_axis(v1_geometry_t *vp) +{ + int y, i, label_tick; + int last_printed_y = -vp->strip_height; + pid_sort_t *pp; + int pid_index; + char *label_fmt; + char tmpbuf [128]; + + /* No pids yet? Outta here */ + if (g_pids == NULL) + return; + + line_batch_start(); + + for (i = 0; i < vp->npids; i++) { + pid_index = vp->first_pid_index + i; + if (pid_index >= g_npids) + break; + + set_color(pid_index); + pp = (g_pids + pid_index); + + label_fmt = get_track_label(pp->pid_value); + snprintf(tmpbuf, sizeof(tmpbuf)-1, label_fmt, pp->pid_value); + + y = i*vp->strip_height + vp->pid_ax_offset; + + /* + * Have we incremented enough space to have another label not + * overlap the previous label? + */ + if (y - last_printed_y > 9) { + /* Draw label */ + tbox(tmpbuf, 0, y +4, TBOX_DRAW_PLAIN+s_print_offset); + + last_printed_y = y; + + /* + * And let the line stick out a bit more to indicate this label + * relates to the following line. + */ + label_tick = 4; + } + else { + label_tick = 0; + } + + /* Draw axis line, but only if the lines aren't too close together */ + if (vp->strip_height > 4) { + line(vp->pid_ax_width - label_tick, y+4*s_print_offset, + vp->total_width, y+4*s_print_offset, + LINE_DRAW_BLACK+s_print_offset); + } + } + + set_color(COLOR_DEFAULT); + line_batch_end(); +} + +/**************************************************************************** +* view1_read_events_callback +* New event data just showed up, reset a few things. +****************************************************************************/ + +void view1_read_events_callback(void) +{ + int max_vis_index; + + s_v1->first_pid_index = 0; + + max_vis_index = 300; + if (max_vis_index > g_nevents) + max_vis_index = g_nevents-1; + + s_v1->minvistime = 0LL; + s_v1->maxvistime = (g_events[g_nevents - 1].time * 9)/ 8; + s_srchindex = 0; + s_srchcode = 0; + s_last_selected_event = 0; + + init_track_colors(); + + recompute_hscrollbar(); + recompute_vscrollbar(); +} + +/**************************************************************************** +* display_event_data +****************************************************************************/ + +static void display_event_data(v1_geometry_t *vp) +{ + int start_index; + int pid_index; + int x, y; + event_t *ep; + event_def_t *edp; + double time_per_pixel; + char tmpbuf[1024]; + GdkRectangle *print_rect; + int *last_x_used; + + /* Happens if one loads the event def header first, for example. */ + if (g_nevents == 0) + return; + + time_per_pixel = dtime_per_pixel(vp); + + start_index = find_event_index (vp->minvistime); + + /* Scrolled too far right? */ + if (start_index >= g_nevents) + return; + + ep = (g_events + start_index); + + if (s_print_offset || summary_mode) { + last_x_used = (int *)g_malloc0(vp->npids * sizeof(int)); + } else { + last_x_used = NULL; + } + + line_batch_start(); + + while (ep < (g_events + g_nevents) && + (ep->time < vp->maxvistime)) { + pid_index = ep->pid->pid_index; + set_color(pid_index); + + /* First filter: pid out of range */ + if ((pid_index < vp->first_pid_index) || + (pid_index >= vp->first_pid_index + vp->npids)) { + ep++; + continue; + } + + /* Second filter: event hidden */ + edp = find_event_definition (ep->code); + if (!edp->selected) { + ep++; + continue; + } + + /* Display it... */ + + pid_index -= vp->first_pid_index; + + y = pid_index*vp->strip_height + vp->event_offset; + + x = vp->pid_ax_width + + (int)(((double)(ep->time - vp->minvistime)) / time_per_pixel); + + if (last_x_used != NULL && x < last_x_used[pid_index]) { + ep++; + continue; + } + + if (ep->flags & (EVENT_FLAG_SELECT | EVENT_FLAG_SEARCHRSLT)) { + if (ep->flags & EVENT_FLAG_SELECT) { + format_popbox_string(tmpbuf, sizeof(tmpbuf), ep, edp); +#ifdef NOTDEF + sprintf(tmpbuf, edp->name); + sprintf(tmpbuf+strlen(tmpbuf), ": "); + sprintf(tmpbuf+strlen(tmpbuf), edp->format, ep->datum); +#endif + } else { + sprintf(tmpbuf, "SEARCH RESULT"); + } + print_rect = tbox(tmpbuf, x, y - vp->pop_offset, + TBOX_DRAW_BOXED+s_print_offset); + line(x, y-vp->pop_offset, x, y, LINE_DRAW_BLACK+s_print_offset); + if (last_x_used != NULL) + last_x_used[pid_index] = x + print_rect->width; + } + if (summary_mode) { + int delta = vp->strip_height / 3; + if (delta < 1) + delta = 1; + y = pid_index*vp->strip_height + vp->pid_ax_offset; + line(x, y - delta, x, y + delta, LINE_DRAW_BLACK); + last_x_used[pid_index] = x + 1; + } else { + sprintf(tmpbuf, "%ld", ep->code); + print_rect = tbox(tmpbuf, x, y, TBOX_DRAW_EVENT+s_print_offset); + if (last_x_used != NULL) + last_x_used[pid_index] = x + print_rect->width; + } + + ep++; + } + if (last_x_used) + g_free(last_x_used); + line_batch_end(); + set_color(COLOR_DEFAULT); +} + +/**************************************************************************** +* display_clear +****************************************************************************/ + +static void display_clear(void) +{ + GdkRectangle update_rect; + + gdk_draw_rectangle (pm, da->style->white_gc, TRUE, + 0, 0, da->allocation.width, + da->allocation.height); + + update_rect.x = 0; + update_rect.y = 0; + update_rect.width = da->allocation.width; + update_rect.height = da->allocation.height; + + gtk_widget_draw (da, &update_rect); +} + +/**************************************************************************** +* display_time_axis +****************************************************************************/ + +static void display_time_axis(v1_geometry_t *vp) +{ + int x, y, i; + int xoffset, nticks; + char tmpbuf [128]; + double unit_divisor; + double time; + char *units; + double time_per_pixel; + + y = vp->npids * vp->strip_height + vp->pid_ax_offset; + + x = vp->pid_ax_width; + + nticks = (vp->total_width - vp->pid_ax_width) / vp->time_ax_spacing; + + time_per_pixel = dtime_per_pixel(vp); + + units = "ns"; + unit_divisor = 1.00; + + if ((vp->maxvistime / unit_divisor) > 1000) { + units = "us"; + unit_divisor = 1000.00; + } + + if ((vp->maxvistime / unit_divisor) > 1000) { + units = "ms"; + unit_divisor = 1000.00*1000.00; + } + if ((vp->maxvistime / unit_divisor) > 1000) { + units = "s"; + unit_divisor = 1000.00*1000.00*1000.00; + } + + /* Draw line */ + line(x, y, vp->total_width, y, LINE_DRAW_BLACK+s_print_offset); + + xoffset = 0; + + for (i = 0; i < nticks; i++) { + /* Tick mark */ + line(x+xoffset, y-3, x+xoffset, y+3, LINE_DRAW_BLACK+s_print_offset); + + time = (double)(x + xoffset - vp->pid_ax_width); + time *= time_per_pixel; + time += (double)(vp->minvistime); + time /= unit_divisor; + + sprintf (tmpbuf, "%.2f%s", time, units); + + tbox(tmpbuf, x+xoffset, y+15, TBOX_DRAW_PLAIN+s_print_offset); + + xoffset += vp->time_ax_spacing; + } +} + +/**************************************************************************** +* clear_scoreboard +* Forget about any temporary displays, they're gone now... +****************************************************************************/ + +static void clear_scoreboard(void) +{ + s_result_up = FALSE; +} + +/**************************************************************************** +* view1_display +****************************************************************************/ + +void view1_display(void) +{ + display_clear(); + display_pid_axis(s_v1); + display_event_data(s_v1); + display_time_axis(s_v1); + clear_scoreboard(); +} + +static gint idle_tag; + +/**************************************************************************** +* view1_display_eventually +****************************************************************************/ + +static void view1_display_eventually(void) +{ + gtk_idle_remove(idle_tag); + idle_tag = 0; + view1_display(); +} + + +/**************************************************************************** +* view1_display_when_idle +****************************************************************************/ + +void view1_display_when_idle(void) +{ + if (idle_tag == 0) { + idle_tag = gtk_idle_add((GtkFunction) view1_display_eventually, 0); + } +} + +/**************************************************************************** +* view1_about +****************************************************************************/ + +void view1_about (char *tmpbuf) +{ + int nsnaps; + snapshot_t *snaps; + + sprintf(tmpbuf+strlen(tmpbuf), "Minvistime %lld\nMaxvistime %lld\n", + s_v1->minvistime, s_v1->maxvistime); + sprintf(tmpbuf+strlen(tmpbuf), "Strip Height %d\n", + s_v1->strip_height); + + for (nsnaps = 0, snaps = s_snapshots; snaps; snaps = snaps->next) { + nsnaps++; + } + sprintf(tmpbuf+strlen(tmpbuf), "%d snapshots in the ring\n", nsnaps); +} diff --git a/src/tools/perftool/c2cpel.c b/src/tools/perftool/c2cpel.c new file mode 100644 index 00000000..38e6fe52 --- /dev/null +++ b/src/tools/perftool/c2cpel.c @@ -0,0 +1,248 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2006-2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/fcntl.h> +#include <sys/mman.h> +#include <unistd.h> +#include <ctype.h> +#include <vppinfra/clib.h> +#include <vppinfra/vec.h> +#include <vppinfra/hash.h> +#include <vppinfra/elog.h> +#include <pwd.h> +#include <stdarg.h> +#include <time.h> +#include "cpel.h" +#include "cpel_util.h" + +static elog_main_t elog_main; + +/* + * convert_clib_file + */ +void convert_clib_file(char *clib_file) +{ + clib_error_t *error = 0; + int i; + elog_main_t *em = &elog_main; + double starttime, delta; + + error = elog_read_file (&elog_main, clib_file); + + if (error) { + clib_warning("%U", format_clib_error, error); + exit (1); + } + + em = &elog_main; + + starttime = em->events[0].time; + + for (i = 0; i < vec_len (em->events); i++) { + elog_event_t *e; /* clib event */ + evt_t *ep; /* xxx2cpel event */ + u8 *s; + u64 timestamp; + elog_event_type_t *t; + u8 *brief_event_name; + u8 *track_name; + int j; + + e = vec_elt_at_index(em->events, i); + + /* Seconds since start of log */ + delta = e->time - starttime; + + /* u64 nanoseconds since start of log */ + timestamp = delta * 1e9; + + s = format (0, "%U%c", format_elog_event, em, e, 0); + + /* allocate an event instance */ + vec_add2(the_events, ep, 1); + ep->timestamp = timestamp; + + /* convert string event code to a real number */ + t = vec_elt_at_index (em->event_types, e->type); + + /* + * Construct a reasonable event name. + * Truncate the format string at the first whitespace break + * or printf format character. + */ + brief_event_name = format (0, "%s", t->format); + + for (j = 0; j < vec_len (brief_event_name); j++) { + if (brief_event_name[j] == ' ' || + brief_event_name[j] == '%' || + brief_event_name[j] == '(') { + brief_event_name[j] = 0; + break; + } + } + /* Throw away that much of the formatted event */ + vec_delete (s, j+1, 0); + + ep->event_id = find_or_add_event(brief_event_name, "%s"); + + track_name = format (0, "%U%c", format_elog_track, em, e, 0); + + ep->track_id = find_or_add_track (track_name); + + ep->datum = find_or_add_strtab(s); + + vec_free (track_name); + vec_free(brief_event_name); + vec_free(s); + } +} + +u8 *vec_basename (char *s) +{ + u8 * rv; + char *cp = s; + + while (*cp) + cp++; + + cp--; + + while (cp > s && *cp != '/') + cp--; + + if (cp > s) + cp++; + + rv = format (0, "%s", cp); + return rv; +} + + +int event_compare (const void *a0, const void *a1) +{ + evt_t *e0 = (evt_t *)a0; + evt_t *e1 = (evt_t *)a1; + + if (e0->timestamp < e1->timestamp) + return -1; + else if (e0->timestamp > e1->timestamp) + return 1; + return 0; +} + +int main (int argc, char **argv) +{ + int curarg=1; + char **inputfiles = 0; + char *outputfile = 0; + FILE *ofp; + + if (argc < 3) + goto usage; + + while (curarg < argc) { + if (!strncmp(argv[curarg], "--input-file", 3)) { + curarg++; + if (curarg < argc) { + vec_add1 (inputfiles, argv[curarg]); + curarg++; + continue; + } + clib_warning("Missing filename after --input-file\n"); + exit (1); + } + + if (!strncmp(argv[curarg], "--output-file", 3)) { + curarg ++; + if (curarg < argc) { + outputfile = argv[curarg]; + curarg ++; + continue; + } + clib_warning("Missing filename after --output-file\n"); + exit(1); + } + vec_add1 (inputfiles, argv[curarg]); + curarg++; + continue; + + usage: + fformat(stderr, + "c2cpel [--input-file] <filename> --output-file <filename>\n"); + exit(1); + } + + if (vec_len(inputfiles) == 0 || outputfile == 0) + goto usage; + + if (vec_len(inputfiles) > 1) + goto usage; + + cpel_util_init(); + + convert_clib_file (inputfiles[0]); + + ofp = fopen (outputfile, "w"); + if (ofp == NULL) { + clib_unix_warning ("couldn't create %s", outputfile); + exit (1); + } + + alpha_sort_tracks(); + fixup_event_tracks(); + + /* + * Four sections: string-table, event definitions, track defs, events. + */ + if (!write_cpel_header(ofp, 4)) { + clib_warning ("Error writing cpel header to %s...\n", outputfile); + unlink(outputfile); + exit(1); + } + + if (!write_string_table(ofp)) { + clib_warning ("Error writing string table to %s...\n", outputfile); + unlink(outputfile); + exit(1); + } + + if (!write_event_defs(ofp)) { + clib_warning ("Error writing event defs to %s...\n", outputfile); + unlink(outputfile); + exit(1); + } + + if (!write_track_defs(ofp)) { + clib_warning ("Error writing track defs to %s...\n", outputfile); + unlink(outputfile); + exit(1); + } + + if (!write_events(ofp, (u64) 1e9)) { + clib_warning ("Error writing events to %s...\n", outputfile); + unlink(outputfile); + exit(1); + + } + fclose(ofp); + exit (0); +} diff --git a/src/tools/perftool/configure.ac b/src/tools/perftool/configure.ac new file mode 100644 index 00000000..f4a98697 --- /dev/null +++ b/src/tools/perftool/configure.ac @@ -0,0 +1,12 @@ +AC_INIT(perftool, 2.0) +AM_INIT_AUTOMAKE +AM_SILENT_RULES([yes]) + +AC_CHECK_LIB([vppinfra], [clib_mem_get_page_size],, + AC_MSG_ERROR([Please install the vpp-lib package])) +AC_CHECK_HEADER([vppinfra/clib.h],, + AC_MSG_ERROR([Please install the vpp-dev package])) + +AM_PROG_LIBTOOL + +AC_OUTPUT([Makefile]) 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..9011bd03 --- /dev/null +++ b/src/tools/perftool/cpeldump.c @@ -0,0 +1,638 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2006-2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/fcntl.h> +#include <sys/mman.h> +#include <unistd.h> +#include <ctype.h> +#include <vppinfra/clib.h> +#include <vppinfra/vec.h> +#include <vppinfra/hash.h> +#include <pwd.h> +#include <stdarg.h> +#include <time.h> +#include "cpel.h" + +char *time_format = "%.03d:%.02d:%.02d:%.03d:%.03d "; +static char version[] = "cpeldump 2.0"; + +#define USEC_PER_MS 1000LL +#define USEC_PER_SECOND (1000*USEC_PER_MS) +#define USEC_PER_MINUTE (60*USEC_PER_SECOND) +#define USEC_PER_HOUR (60*USEC_PER_MINUTE) + +uword *the_strtab_hash; /* (name, base-VA) hash of all string tables */ +uword *the_evtdef_hash; /* (event-id, event-definition) hash */ +uword *the_trackdef_hash; /* (track-id, track-definition) hash */ + +int widest_name_format=5; +int widest_track_format=5; + +typedef struct bound_event_ { + u32 event_code; + u8 *event_str; + u8 *datum_str; + u32 is_strtab_ref; +} bound_event_t; + +bound_event_t *bound_events; + +typedef struct bound_track_ { + u32 track; + u8 *track_str; +} bound_track_t; + +bound_track_t *bound_tracks; + +void fatal(char *s) +{ + fprintf(stderr, "%s", s); + exit(1); +} + +typedef enum { + PASS1=1, + PASS2=2, +} pass_t; + +typedef struct { + int (*pass1)(cpel_section_header_t *, int, FILE *); + int (*pass2)(cpel_section_header_t *, int, FILE *); +} section_processor_t; + +int bad_section(cpel_section_header_t *sh, int verbose, FILE *ofp) +{ + fprintf(ofp, "Bad (type 0) section, skipped...\n"); + return(0); +} + +int noop_pass(cpel_section_header_t *sh, int verbose, FILE *ofp) +{ + return(0); +} + +int strtab_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp) +{ + uword *p; + u8 *strtab_data_area = (u8 *)(sh+1); + + /* Multiple string tables with the same name are Bad... */ + p = hash_get_mem(the_strtab_hash, strtab_data_area); + if (p) { + fprintf(ofp, "Duplicate string table name %s", strtab_data_area); + } + /* + * Looks funny, but we really do want key = first string in the + * table, value = address(first string in the table) + */ + hash_set_mem(the_strtab_hash, strtab_data_area, strtab_data_area); + if (verbose) { + fprintf(stderr, "String Table %s\n", strtab_data_area); + } + return(0); +} + +int evtdef_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp) +{ + int i, nevents; + event_definition_section_header_t *edh; + event_definition_t *ep; + u8 *this_strtab; + u32 event_code; + uword *p; + bound_event_t *bp; + int thislen; + + edh = (event_definition_section_header_t *)(sh+1); + nevents = ntohl(edh->number_of_event_definitions); + + if (verbose) { + fprintf(stderr, "Event Definition Section: %d definitions\n", + nevents); + } + + p = hash_get_mem(the_strtab_hash, edh->string_table_name); + if (!p) { + fprintf(ofp, "Fatal: couldn't find string table\n"); + return(1); + } + this_strtab = (u8 *)p[0]; + + ep = (event_definition_t *)(edh+1); + + for (i = 0; i < nevents; i++) { + event_code = ntohl(ep->event); + p = hash_get(the_evtdef_hash, event_code); + if (p) { + fprintf(ofp, "Event %d redefined, retain first definition\n", + event_code); + continue; + } + vec_add2(bound_events, bp, 1); + bp->event_code = event_code; + bp->event_str = this_strtab + ntohl(ep->event_format); + bp->datum_str = this_strtab + ntohl(ep->datum_format); + bp->is_strtab_ref = 0; + /* Decide if the datum format is a %s format => strtab reference */ + { + int j; + int seen_percent=0; + + for (j = 0; j < strlen((char *)bp->datum_str); j++) { + if (bp->datum_str[j] == '%'){ + seen_percent=1; + continue; + } + if (seen_percent && bp->datum_str[j] == 's') { + bp->is_strtab_ref = 1; + } + } + } + + hash_set(the_evtdef_hash, event_code, bp - bound_events); + + thislen = strlen((char *)bp->event_str); + if (thislen > widest_name_format) + widest_name_format = thislen; + + ep++; + } + return (0); +} + +int trackdef_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp) +{ + int i, nevents; + track_definition_section_header_t *tdh; + track_definition_t *tp; + u8 *this_strtab; + u32 track_code; + uword *p; + bound_track_t *btp; + int thislen; + + tdh = (track_definition_section_header_t *)(sh+1); + nevents = ntohl(tdh->number_of_track_definitions); + + if (verbose) { + fprintf(stderr, "Track Definition Section: %d definitions\n", + nevents); + } + + p = hash_get_mem(the_strtab_hash, tdh->string_table_name); + if (!p) { + fprintf(ofp, "Fatal: couldn't find string table\n"); + return(1); + } + this_strtab = (u8 *)p[0]; + + tp = (track_definition_t *)(tdh+1); + + for (i = 0; i < nevents; i++) { + track_code = ntohl(tp->track); + p = hash_get(the_trackdef_hash, track_code); + if (p) { + fprintf(ofp, "track %d redefined, retain first definition\n", + track_code); + continue; + } + vec_add2(bound_tracks, btp, 1); + btp->track = track_code; + btp->track_str = this_strtab + ntohl(tp->track_format); + hash_set(the_trackdef_hash, track_code, btp - bound_tracks); + + thislen = strlen((char *)btp->track_str); + if (thislen > widest_track_format) + widest_track_format = thislen; + tp++; + } + return (0); +} + +int unsupported_pass (cpel_section_header_t *sh, int verbose, FILE *ofp) +{ + if (verbose) { + fprintf(stderr, "Unsupported type %d section\n", + ntohl(sh->section_type)); + } + return(0); +} + +int event_pass2(cpel_section_header_t *sh, int verbose, FILE *ofp) +{ + event_section_header_t *eh; + f64 ticks_per_us; + u32 event_code, track_code; + u64 starttime = 0xFFFFFFFFFFFFFFFFULL; + int nevents; + int i; + uword *p; + event_entry_t *ep; + u64 now; + u64 delta; + u32 hours, minutes, seconds, msec, usec; + u32 time0, time1; + double d; + bound_event_t *bp; + bound_event_t generic_event; + bound_track_t *tp=0; + bound_track_t generic_track; + u32 last_track_code; + u8 *s, *evtpad, *trackpad; + u8 *this_strtab; + + generic_event.event_str = (u8 *)"%d"; + generic_event.datum_str = (u8 *)"0x%08x"; + generic_event.is_strtab_ref = 0; + + generic_track.track_str = (u8 *)"%d"; + last_track_code = 0xdeadbeef; + + eh = (event_section_header_t *)(sh+1); + nevents = ntohl(eh->number_of_events); + ticks_per_us = ((double)ntohl(eh->clock_ticks_per_second)) / 1e6; + + if (verbose) { + fprintf(stderr, "Event section: %d events, %.3f ticks_per_us\n", + nevents, ticks_per_us); + } + + ep = (event_entry_t *)(eh+1); + + p = hash_get_mem(the_strtab_hash, eh->string_table_name); + if (!p) { + fprintf(ofp, "Fatal: couldn't find string table\n"); + return(1); + } + this_strtab = (u8 *)p[0]; + + evtpad = format(0, "%%-%ds ", widest_name_format); + vec_add1(evtpad, 0); + trackpad = format(0, "%%-%ds ", widest_track_format); + vec_add1(trackpad, 0); + + for (i = 0; i < nevents; i++) { + time0 = ntohl (ep->time[0]); + time1 = ntohl (ep->time[1]); + + now = (((u64) time0)<<32) | time1; + + /* Convert from bus ticks to usec */ + d = now; + d /= ticks_per_us; + + now = d; + + if (starttime == 0xFFFFFFFFFFFFFFFFULL) + starttime = now; + + delta = now - starttime; + + /* Delta = time since first event, in usec */ + + hours = delta / USEC_PER_HOUR; + if (hours) + delta -= ((u64) hours * USEC_PER_HOUR); + minutes = delta / USEC_PER_MINUTE; + if (minutes) + delta -= ((u64) minutes * USEC_PER_MINUTE); + seconds = delta / USEC_PER_SECOND; + if (seconds) + delta -= ((u64) seconds * USEC_PER_SECOND); + msec = delta / USEC_PER_MS; + if (msec) + delta -= ((u64) msec * USEC_PER_MS); + + usec = delta; + + /* Output the timestamp */ + fprintf(ofp, time_format, hours, minutes, seconds, msec, usec); + + /* output the track */ + track_code = ntohl(ep->track); + + if (track_code != last_track_code) { + p = hash_get(the_trackdef_hash, track_code); + if (p) { + tp = &bound_tracks[p[0]]; + } else { + tp = &generic_track; + } + } + s = format(0, (char *)tp->track_str, track_code); + vec_add1(s, 0); + fprintf(ofp, (char *)trackpad, s); + vec_free(s); + + /* output the event and datum */ + if (0 && verbose) { + fprintf(stderr, "raw event code %d, raw event datum 0x%x\n", + ntohl(ep->event_code), ntohl(ep->event_datum)); + } + + event_code = ntohl(ep->event_code); + p = hash_get(the_evtdef_hash, event_code); + if (p) { + bp = &bound_events[p[0]]; + } else { + bp = &generic_event; + } + s = format(0, (char *)bp->event_str, ntohl(ep->event_code)); + vec_add1(s, 0); + fprintf(ofp, (char *)evtpad, s); + vec_free(s); + if (bp->is_strtab_ref) { + fprintf(ofp, (char *) bp->datum_str, + &this_strtab[ntohl(ep->event_datum)]); + } else { + fprintf(ofp, (char *) bp->datum_str, ntohl(ep->event_datum)); + } + fputs("\n", ofp); + ep++; + } + vec_free(evtpad); + vec_free(trackpad); + return(0); +} + +/* + * Note: If necessary, add passes / columns to this table to + * handle section order dependencies. + */ + +section_processor_t processors[CPEL_NUM_SECTION_TYPES+1] = +{ + {bad_section, noop_pass}, /* type 0 -- f**ked */ + {strtab_pass1, noop_pass}, /* type 1 -- STRTAB */ + {unsupported_pass, noop_pass}, /* type 2 -- SYMTAB */ + {evtdef_pass1, noop_pass}, /* type 3 -- EVTDEF */ + {trackdef_pass1, noop_pass}, /* type 4 -- TRACKDEF */ + {noop_pass, event_pass2}, /* type 5 -- EVENTS */ +}; + + +int process_section(cpel_section_header_t *sh, int verbose, FILE *ofp, + pass_t pass) +{ + u32 type; + type = ntohl(sh->section_type); + int rv; + int (*fp)(cpel_section_header_t *, int, FILE *); + + if (type > CPEL_NUM_SECTION_TYPES) { + fprintf(stderr, "Unknown section type %d\n", type); + return(1); + } + switch(pass) { + case PASS1: + fp = processors[type].pass1; + break; + + case PASS2: + fp = processors[type].pass2; + break; + + default: + fprintf(stderr, "Unknown pass %d\n", pass); + return(1); + } + + rv = (*fp)(sh, verbose, ofp); + + return(rv); +} + +int cpel_dump_file_header(cpel_file_header_t *fh, int verbose, FILE *ofp) +{ + time_t file_time; + + if (verbose) { + fprintf(stderr, "CPEL file: %s-endian, version %d\n", + ((fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) ? + "little" : "big"), + fh->endian_version & CPEL_FILE_VERSION_MASK); + + file_time = ntohl(fh->file_date); + + fprintf(stderr, "File created %s", ctime(&file_time)); + fprintf(stderr, "File has %d sections\n", + ntohs(fh->nsections)); + } + + return(0); +} + + +int cpel_dump(u8 *cpel, int verbose, FILE *ofp) +{ + cpel_file_header_t *fh; + cpel_section_header_t *sh; + u16 nsections; + u32 section_size; + int i; + + /* First, the file header */ + fh = (cpel_file_header_t *)cpel; + if (fh->endian_version != CPEL_FILE_VERSION) { + if (fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) { + fprintf(stderr, "Little endian data format not supported\n"); + return(1); + } + fprintf(stderr, "Unsupported file version 0x%x\n", + fh->endian_version); + return(1); + } + cpel_dump_file_header(fh, verbose, ofp); + nsections = ntohs(fh->nsections); + + /* + * Take two passes through the file. PASS1 builds + * data structures, PASS2 actually dumps the file. + * Just in case the sections are in an unobvious order. + */ + sh = (cpel_section_header_t *)(fh+1); + for (i = 0; i < nsections; i++) { + section_size = ntohl(sh->data_length); + + if(verbose) { + fprintf(stderr, + "Section type %d, size %d\n", ntohl(sh->section_type), + section_size); + } + + if(process_section(sh, verbose, ofp, PASS1)) + return(1); + + sh++; + sh = (cpel_section_header_t *)(((u8 *)sh)+section_size); + } + + sh = (cpel_section_header_t *)(fh+1); + for (i = 0; i < nsections; i++) { + if(process_section(sh, verbose, ofp, PASS2)) + return(1); + section_size = ntohl(sh->data_length); + sh++; + sh = (cpel_section_header_t *)(((u8 *)sh)+section_size); + } + return(0); +} + + +char *mapfile (char *file) +{ + struct stat statb; + char *rv; + int maphfile; + size_t mapfsize; + + maphfile = open (file, O_RDONLY); + + if (maphfile < 0) + { + fprintf (stderr, "Couldn't read %s, skipping it...\n", file); + return (NULL); + } + + if (fstat (maphfile, &statb) < 0) + { + fprintf (stderr, "Couldn't get size of %s, skipping it...\n", file); + return (NULL); + } + + /* Don't try to mmap directories, FIFOs, semaphores, etc. */ + if (! (statb.st_mode & S_IFREG)) { + fprintf (stderr, "%s is not a regular file, skipping it...\n", file); + return (NULL); + } + + mapfsize = statb.st_size; + + if (mapfsize < 3) + { + fprintf (stderr, "%s zero-length, skipping it...\n", file); + close (maphfile); + return (NULL); + } + + rv = mmap (0, mapfsize, PROT_READ, MAP_SHARED, maphfile, 0); + + if (rv == 0) + { + fprintf (stderr, "%s problem mapping, I quit...\n", file); + exit (-1); + } + close (maphfile); + return (rv); +} + +/* + * main + */ +int main (int argc, char **argv) +{ + char *cpel_file = 0; + char *outputfile = 0; + FILE *ofp; + char *cpel; + int verbose=0; + int curarg=1; + + while (curarg < argc) { + if (!strncmp(argv[curarg], "--input-file", 3)) { + curarg++; + if (curarg < argc) { + cpel_file = argv[curarg]; + curarg++; + continue; + } + fatal("Missing filename after --input-file\n"); + } + if (!strncmp(argv[curarg], "--output-file", 3)) { + curarg ++; + if (curarg < argc) { + outputfile = argv[curarg]; + curarg ++; + continue; + } + fatal("Missing filename after --output-file\n"); + } + if (!strncmp(argv[curarg], "--verbose", 3)) { + curarg++; + verbose = 1; + continue; + } + + usage: + fprintf(stderr, + "cpeldump --input-file <filename> [--output-file <filename>]\n"); + fprintf(stderr, "%s\n", version); + exit(1); + } + + if (cpel_file == 0) + goto usage; + + cpel = mapfile(cpel_file); + if (cpel == 0) { + fprintf(stderr, "Couldn't map %s...\n", cpel_file); + exit(1); + } + + if (!outputfile) { + ofp = fdopen(1, "w"); + if (ofp == NULL) { + fprintf(stderr, "Couldn't fdopen(1)?\n"); + exit(1); + } + } else { + ofp = fopen(outputfile, "w"); + if (ofp == NULL) { + fprintf(stderr, "Couldn't create %s...\n", outputfile); + exit(1); + } + } + + the_strtab_hash = hash_create_string (0, sizeof (uword)); + the_evtdef_hash = hash_create (0, sizeof (uword)); + the_trackdef_hash = hash_create (0, sizeof (uword)); + +#ifdef TEST_TRACK_INFO + { + bound_track_t *btp; + vec_add2(bound_tracks, btp, 1); + btp->track = 0; + btp->track_str = "cpu %d"; + hash_set(the_trackdef_hash, 0, btp - bound_tracks); + hash_set(the_trackdef_hash, 1, btp - bound_tracks); + } +#endif + + if (cpel_dump((u8 *)cpel, verbose, ofp)) { + if (outputfile) + unlink(outputfile); + } + + fclose(ofp); + return(0); +} diff --git a/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/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 Binary files differnew file mode 100644 index 00000000..b0f35958 --- /dev/null +++ b/src/tools/perftool/new.cpel diff --git a/src/tools/perftool/new.elog b/src/tools/perftool/new.elog Binary files differnew file mode 100644 index 00000000..2d99bb16 --- /dev/null +++ b/src/tools/perftool/new.elog 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/configure.ac b/src/tools/vppapigen/configure.ac new file mode 100644 index 00000000..16ad59d2 --- /dev/null +++ b/src/tools/vppapigen/configure.ac @@ -0,0 +1,14 @@ +# -*- Autoconf -*- +# Copyright (c) 2008 by cisco Systems, Inc. +# All rights reserved. +# Process this file with autoconf to produce a configure script. + +AC_INIT(vppapigen, 1.0) +AM_INIT_AUTOMAKE +AM_SILENT_RULES([yes]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_YACC + +AC_OUTPUT([Makefile]) diff --git a/src/tools/vppapigen/gram.y b/src/tools/vppapigen/gram.y new file mode 100644 index 00000000..de26af8d --- /dev/null +++ b/src/tools/vppapigen/gram.y @@ -0,0 +1,90 @@ +%{ +/* + * 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 + +%% + +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 long) + ((unsigned long long) $1 + | (unsigned long long) $2);} + | flag {$$ = $1;} + ; + +flag: + MANUAL_PRINT {$$ = $1;} + | MANUAL_ENDIAN {$$ = $1;} + | DONT_TRACE {$$ = $1;} + | TYPEONLY {$$ = $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..733942ad --- /dev/null +++ b/src/tools/vppapigen/lex.c @@ -0,0 +1,1067 @@ +/* + *------------------------------------------------------------------ + * 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" + +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; + +/* + * 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) +{ + return ((char)(getc(ifp) & 0x7f)); +} + +/* + * 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 (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 (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 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! */ +{ + {"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_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..a0fdc735 --- /dev/null +++ b/src/tools/vppapigen/lex.h @@ -0,0 +1,50 @@ +/* + *------------------------------------------------------------------ + * 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); + +#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..260c6f2e --- /dev/null +++ b/src/tools/vppapigen/node.c @@ -0,0 +1,1527 @@ +/* + *------------------------------------------------------------------ + * 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)(u64)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); + 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); + 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; + 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) + +{ + char *datestring = ctime(&starttime); + 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)(u64)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..297d6036 --- /dev/null +++ b/src/tools/vppapigen/node.h @@ -0,0 +1,94 @@ +/* + *------------------------------------------------------------------ + * 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, +}; + +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) + +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 */ |