/*
* Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
* 2002, 2003, 2004
* Ohio University.
*
* ---
*
* Starting with the release of tcptrace version 6 in 2001, tcptrace
* is licensed under the GNU General Public License (GPL). We believe
* that, among the available licenses, the GPL will do the best job of
* allowing tcptrace to continue to be a valuable, freely-available
* and well-maintained tool for the networking community.
*
* Previous versions of tcptrace were released under a license that
* was much less restrictive with respect to how tcptrace could be
* used in commercial products. Because of this, I am willing to
* consider alternate license arrangements as allowed in Section 10 of
* the GNU GPL. Before I would consider licensing tcptrace under an
* alternate agreement with a particular individual or company,
* however, I would have to be convinced that such an alternative
* would be to the greater benefit of the networking community.
*
* ---
*
* This file is part of Tcptrace.
*
* Tcptrace was originally written and continues to be maintained by
* Shawn Ostermann with the help of a group of devoted students and
* users (see the file 'THANKS'). The work on tcptrace has been made
* possible over the years through the generous support of NASA GRC,
* the National Science Foundation, and Sun Microsystems.
*
* Tcptrace is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tcptrace is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tcptrace (in the file 'COPYING'); if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Author: Shawn Ostermann
* School of Electrical Engineering and Computer Science
* Ohio University
* Athens, OH
* ostermann@cs.ohiou.edu
* http://www.tcptrace.org/
*/
#include "tcptrace.h"
static char const GCC_UNUSED copyright[] =
"@(#)Copyright (c) 2004 -- Ohio University.\n";
static char const GCC_UNUSED rcsid[] =
"@(#)$Header: /usr/local/cvs/tcptrace/plotter.c,v 5.18 2003/11/19 14:38:04 sdo Exp $";
/* info that I keep about each plotter */
struct plotter_info {
MFILE *fplot; /* the file that hold the plot */
tcb *p2plast; /* the TCB that this goes with (if any) */
timeval zerotime; /* first time stamp in this plot (see -z) */
char *filename; /* redundant copy of name for debugging */
Bool header_done; /* Flag indicating plotter header written to file */
Bool axis_switched; /* Switch x & y axis types.
* (Needed for Time Line Charts,
* Default = FALSE)
*/
char *title; /* Plotter title */
char *xlabel; /* Plotter x-axis label */
char *ylabel; /* Plotter y-axis label */
};
/* locally global parameters */
static int max_plotters;
static PLOTTER plotter_ix = NO_PLOTTER;
static char *temp_color = NULL;
static struct plotter_info *pplotters;
/* local routine declarations */
static char *xp_timestamp(PLOTTER pl, struct timeval time);
static char *TSGPlotName(tcb *plast, PLOTTER, char *suffix);
static void DoPlot(PLOTTER pl, char *fmt, ...);
static void WritePlotHeader(PLOTTER pl);
static void CallDoPlot(PLOTTER pl, char *plot_cmd, int plot_argc, ...);
/*
* Return a string suitable for use as a timestamp in the xplot output.
* sdo fix: originally, we were just plotting to the accuracy of 1/10 ms
* this was mostly to help keep the .xpl files from being too large. However,
* on faster networks this just isn't enough, so we'll now use all 6 digits
* of the microsecond counter. Note that there's no guarantee that the
* timestamps are really all that accurate!
*/
static char *
xp_timestamp(
PLOTTER pl,
struct timeval time)
{
static char bufs[4][20]; /* several of them for multiple calls in one printf */
static int bufix = 0;
unsigned secs;
unsigned usecs;
unsigned decimal;
char *pbuf;
struct plotter_info *ppi;
ppi = &pplotters[pl];
/* see if we're graphing from "0" OR if the axis type is switched */
if (graph_time_zero || ppi->axis_switched) {
if (ZERO_TIME(&ppi->zerotime)) {
/* set "zero point" */
ppi->zerotime = time;
}
/* (in)sanity check */
if (tv_lt(time,ppi->zerotime)) {
fprintf(stderr,"Internal error in plotting (plot file '%s')...\n\
ZERO-based X-axis plotting requested and elements are not plotted in\n\
increasing time order. Try without the '-z' flag\n",
ppi->filename);
/* exit(-5); */
time.tv_sec = time.tv_usec = 0;
} else {
/* computer offset from first plotter point */
tv_sub(&time, ppi->zerotime);
}
}
/* calculate time components */
secs = time.tv_sec;
usecs = time.tv_usec;
decimal = usecs;
/* use one of 4 rotating static buffers (for multiple calls per printf) */
bufix = (bufix+1)%4;
pbuf = bufs[bufix];
snprintf(pbuf,sizeof(bufs[bufix]),"%u.%06u",secs,decimal);
return(pbuf);
}
void
plot_init(void)
{
max_plotters = 256; /* just a default, make more on the fly */
pplotters = MallocZ(max_plotters * sizeof(struct plotter_info));
}
static void
plotter_makemore(void)
{
int new_max_plotters = max_plotters * 4;
if (debug)
fprintf(stderr,"plotter: making more space for %d total plotters\n",
new_max_plotters);
/* reallocate the memory to make more space */
pplotters = ReallocZ(pplotters,
max_plotters * sizeof(struct plotter_info),
new_max_plotters * sizeof(struct plotter_info));
max_plotters = new_max_plotters;
}
/* max number of letters in endpoint name */
/* (8 allows 26**8 different endpoints (209,000,000,000)
probably plenty for now!!!!!) */
/* #define MAX_HOSTLETTER_LEN 8
Moving this definition to tcptrace.h so other modules can use it. */
char *
HostLetter(
llong ix)
{
static char name[MAX_HOSTLETTER_LEN+1];
static char *pname;
/* basically, just convert to base 26 */
pname = &name[sizeof(name)-1];
*pname-- = '\00';
while (pname >= name) {
unsigned digit = ix % 26;
*pname-- = 'a'+digit;
ix = (ix / 26) - 1;
if (ix == -1)
return(pname+1);
}
fprintf(stderr,"Fatal, too many hosts to name (max length %d)\n\nNOTE:\nIf you are using gcc version 2.95.3, then this may be a compiler bug. This particular version\nis known to generate incorrect assembly code when used with CCOPT=-O2.\nSuggested fixes are:\n 1. Update gcc to the latest version and recompile tcptrace.\n 2. Use the same version of gcc, but edit the tcptrace Makefile, setting CCOPT=-O instead of\n CCOPT=-O2, and then recompile tcptrace.\nEither of these steps should hopefully fix the problem.\n\n", MAX_HOSTLETTER_LEN);
exit(-1);
return(NULL); /* NOTREACHED */
}
char *
NextHostLetter(void)
{
static llong count = 0;
return(HostLetter(count++));
}
static char *
TSGPlotName(
tcb *plast,
PLOTTER pl,
char *suffix)
{
static char filename[25];
snprintf(filename,sizeof(filename),"%s2%s%s",
plast->host_letter, plast->ptwin->host_letter, suffix);
return(filename);
}
static void
DoPlot(
PLOTTER pl,
char *fmt,
...)
{
va_list ap;
MFILE *f = NULL;
struct plotter_info *ppi;
va_start(ap,fmt);
/* if (!graph_tsg) */
/* return; */
if (pl == NO_PLOTTER) {
va_end(ap);
return;
}
if (pl > plotter_ix) {
fprintf(stderr,"Illegal plotter: %d\n", pl);
exit(-1);
}
ppi = &pplotters[pl];
if ((f = ppi->fplot) == NULL) {
va_end(ap);
return;
}
/* Write the plotter header if not already written */
if(!ppi->header_done)
WritePlotHeader(pl);
Mvfprintf(f,fmt,ap);
if (temp_color) {
Mfprintf(f," %s",temp_color);
temp_color = NULL;
}
Mfprintf (f,"\n");
va_end(ap);
return;
}
PLOTTER
new_plotter(
tcb *plast,
char *filename, /* if NULL, use default name from plast */
char *title,
char *xlabel,
char *ylabel,
char *suffix)
{
PLOTTER pl;
MFILE *f;
struct plotter_info *ppi;
++plotter_ix;
if (plotter_ix >= max_plotters) {
plotter_makemore();
}
pl = plotter_ix;
ppi = &pplotters[pl];
if (filename == NULL)
filename = TSGPlotName(plast,pl,suffix);
else if (suffix != NULL) {
char buf[100];
snprintf(buf,sizeof(buf),"%s%s", filename, suffix);
filename = buf;
}
if (debug)
fprintf(stderr,"Plotter %d file is '%s'\n", pl, filename);
if ((f = Mfopen(filename,"w")) == NULL) {
perror(filename);
return(NO_PLOTTER);
}
ppi->fplot = f;
ppi->p2plast = plast;
ppi->filename = strdup(filename);
ppi->axis_switched = FALSE;
ppi->header_done = FALSE;
/* Save these fields to be writtn to the plotter header later in DoPlot() */
ppi->title = strdup(title);
ppi->xlabel = strdup(xlabel);
ppi->ylabel = strdup(ylabel);
return(pl);
}
void
plotter_done(void)
{
PLOTTER pl;
MFILE *f;
char *fname;
static struct dstring *xplot_cmd_buff=NULL;
if(plotter_ix>0) {
if(xplot_all_files) {
xplot_cmd_buff=DSNew();
DSAppendString(xplot_cmd_buff,"xplot");
DSAppendString(xplot_cmd_buff," ");
if(xplot_args!=NULL) {
DSAppendString(xplot_cmd_buff,xplot_args);
DSAppendString(xplot_cmd_buff," ");
}
}
}
for (pl = 0; pl <= plotter_ix; ++pl) {
struct plotter_info *ppi = &pplotters[pl];
if ((f = ppi->fplot) == NULL)
continue;
/* Write the plotter header if not already written */
if(!ppi->header_done)
WritePlotHeader(pl);
if (!ignore_non_comp ||
((ppi->p2plast != NULL) && (ConnComplete(ppi->p2plast->ptp)))) {
Mfprintf(f,"go\n");
Mfclose(f);
} else {
fname = ppi->p2plast->tsg_plotfile;
if (debug)
fprintf(stderr,"Removing incomplete plot file '%s'\n",
fname);
Mfclose(f);
if (unlink(fname) != 0)
perror(fname);
}
if(xplot_all_files){
if(output_file_dir!=NULL) {
DSAppendString(xplot_cmd_buff,output_file_dir);
DSAppendString(xplot_cmd_buff,"/");
}
DSAppendString(xplot_cmd_buff,ppi->filename);
DSAppendString(xplot_cmd_buff," ");
}
}
if(plotter_ix>0) {
if(xplot_all_files) {
fprintf(stdout,"%s\n",DSVal(xplot_cmd_buff));
system(DSVal(xplot_cmd_buff));
DSDestroy(&xplot_cmd_buff);
}
}
}
void
plotter_temp_color(
PLOTTER pl,
char *color)
{
if (colorplot)
temp_color = color;
}
void
plotter_perm_color(
PLOTTER pl,
char *color)
{
if (colorplot)
CallDoPlot(pl, color, 0);
}
void
plotter_line(
PLOTTER pl,
struct timeval t1,
u_long x1,
struct timeval t2,
u_long x2)
{
CallDoPlot(pl,"line", 4, t1, x1, t2, x2);
}
void
plotter_dline(
PLOTTER pl,
struct timeval t1,
u_long x1,
struct timeval t2,
u_long x2)
{
CallDoPlot(pl,"dline", 4, t1, x1, t2, x2);
}
void
plotter_diamond(
PLOTTER pl,
struct timeval t,
u_long x)
{
CallDoPlot(pl,"diamond", 2, t, x);
}
void
plotter_dot(
PLOTTER pl,
struct timeval t,
u_long x)
{
CallDoPlot(pl,"dot", 2, t, x);
}
void
plotter_plus(
PLOTTER pl,
struct timeval t,
u_long x)
{
CallDoPlot(pl,"plus", 2, t, x);
}
void
plotter_box(
PLOTTER pl,
struct timeval t,
u_long x)
{
CallDoPlot(pl,"box", 2, t, x);
}
void
plotter_arrow(
PLOTTER pl,
struct timeval t,
u_long x,
char dir)
{
char arrow_type[7];
snprintf(arrow_type, sizeof(arrow_type), "%carrow", dir);
CallDoPlot(pl, arrow_type, 2, t, x);
}
void
plotter_uarrow(
PLOTTER pl,
struct timeval t,
u_long x)
{
plotter_arrow(pl,t,x,'u');
}
void
plotter_darrow(
PLOTTER pl,
struct timeval t,
u_long x)
{
plotter_arrow(pl,t,x,'d');
}
void
plotter_rarrow(
PLOTTER pl,
struct timeval t,
u_long x)
{
plotter_arrow(pl,t,x,'r');
}
void
plotter_larrow(
PLOTTER pl,
struct timeval t,
u_long x)
{
plotter_arrow(pl,t,x,'l');
}
void
plotter_tick(
PLOTTER pl,
struct timeval t,
u_long x,
char dir)
{
char tick_type[6];
snprintf(tick_type, sizeof(tick_type), "%ctick", dir);
CallDoPlot(pl, tick_type, 2, t, x);
}
void
plotter_dtick(
PLOTTER pl,
struct timeval t,
u_long x)
{
plotter_tick(pl,t,x,'d');
}
void
plotter_utick(
PLOTTER pl,
struct timeval t,
u_long x)
{
plotter_tick(pl,t,x,'u');
}
void
plotter_ltick(
PLOTTER pl,
struct timeval t,
u_long x)
{
plotter_tick(pl,t,x,'l');
}
void
plotter_rtick(
PLOTTER pl,
struct timeval t,
u_long x)
{
plotter_tick(pl,t,x,'r');
}
void
plotter_htick(
PLOTTER pl,
struct timeval t,
u_long x)
{
plotter_tick(pl,t,x,'h');
}
void
plotter_vtick(
PLOTTER pl,
struct timeval t,
u_long x)
{
plotter_tick(pl,t,x,'v');
}
/* don't plot ANYTHING, just make sure ZERO point is set! */
void
plotter_nothing(
PLOTTER pl,
struct timeval t)
{
char *ret;
ret = xp_timestamp(pl,t);
if (debug > 10)
printf("plotter_nothing(%s) gets '%s'\n", ts2ascii(&t), ret);
}
void
plotter_text(
PLOTTER pl,
struct timeval t,
u_long x,
char *where,
char *str)
{
char text_type[6];
snprintf(text_type, sizeof(text_type), "%stext", where);
CallDoPlot(pl, text_type, 2, t, x);
/* fix by Bill Fenner - Wed Feb 5, 1997, thanks */
/* This is a little ugly. Text commands take 2 lines. */
/* A temporary color could have been */
/* inserted after that line, but would NOT be inserted after */
/* the next line, so we'll be OK. I can't think of a better */
/* way right now, and this works fine (famous last words) */
CallDoPlot(pl, str, 0);
}
void
plotter_invisible(
PLOTTER pl,
struct timeval t,
u_long x)
{
CallDoPlot(pl,"invisible", 2, t, x);
}
/* high-level line-drawing package */
struct pl_line {
char *color;
char *label;
int last_y;
timeval last_time;
PLOTTER plotter;
Bool labelled;
};
PLINE
new_line(
PLOTTER plotter,
char *label,
char *color)
{
struct pl_line *pl;
pl = MallocZ(sizeof(struct pl_line));
pl->plotter = plotter;
pl->label = label;
pl->color = color;
return(pl);
}
void
extend_line(
PLINE pline,
timeval xval,
int yval)
{
PLOTTER p;
if (!pline)
return;
p = pline->plotter;
#ifdef OLD
/* attach a label to the first non-zero point */
if (!pline->labelled) {
if (yval != 0) {
plotter_temp_color(p, pline->color);
plotter_text(p, xval, yval, "l", pline->label);
pline->labelled = 1;
}
}
#endif
/* attach a label midway on the first line segment above 0 */
/* for whom the second point is NOT 0 */
if (!pline->labelled) {
if ((yval != 0) && (!ZERO_TIME(&pline->last_time))) {
timeval tv_elapsed;
timeval tv_avg;
int avg_yval;
/* computer elapsed time for these 2 points */
tv_elapsed = xval;
tv_sub(&tv_elapsed,pline->last_time);
/* divide elapsed time by 2 */
tv_elapsed.tv_sec /= 2;
tv_elapsed.tv_usec /= 2;
/* add 1/2 of the elapsed time to the oldest point */
/* (giving us the average time) */
tv_avg = pline->last_time;
tv_add(&tv_avg, tv_elapsed);
/* average the Y values */
avg_yval = (1 + pline->last_y+yval)/2;
/* (rounding UP, please) */
/* draw the label */
plotter_temp_color(p, pline->color);
plotter_text(p, tv_avg, avg_yval, "l", pline->label);
/* remember that it's been done */
pline->labelled = 1;
}
}
/* draw a dot at the current point */
plotter_perm_color(p, pline->color);
plotter_dot(p, xval, yval);
/* if this isn't the FIRST point, connect with a line */
if (!ZERO_TIME(&pline->last_time)) {
plotter_line(p,
xval, yval,
pline->last_time, pline->last_y);
}
/* remember this point for the next line segment */
pline->last_time = xval;
pline->last_y = yval;
}
/* This function may be called with 0, 2 or 4 arguments depending on plot command. */
static void
CallDoPlot(
PLOTTER pl,
char *plot_cmd,
int plot_argc,
...)
{
struct timeval t1;
u_long x1 = 0;
struct timeval t2;
u_long x2 = 0;
va_list ap;
struct plotter_info *ppi;
char fmt[200];
if (pl == NO_PLOTTER)
return;
if (pl > plotter_ix) {
fprintf(stderr,"Illegal plotter: %d\n", pl);
exit(-1);
}
ppi = &pplotters[pl];
/* Get the arguments from the variable list */
va_start(ap, plot_argc);
if(plot_argc > 0)
{
t1 = va_arg(ap, struct timeval);
x1 = va_arg(ap, u_long);
}
if(plot_argc > 2)
{
t2 = va_arg(ap, struct timeval);
x2 = va_arg(ap, u_long);
}
va_end(ap);
memset(fmt, 0, sizeof(fmt));
if(ppi->axis_switched) {
switch(plot_argc) {
case 0:
snprintf(fmt, sizeof(fmt), "%s", plot_cmd);
DoPlot(pl, fmt);
break;
case 2:
snprintf(fmt, sizeof(fmt), "%s %%u -%%s", plot_cmd);
DoPlot(pl, fmt,
x1, xp_timestamp(pl,t1));
break;
case 4:
snprintf(fmt, sizeof(fmt), "%s %%u -%%s %%u -%%s", plot_cmd);
DoPlot(pl, fmt,
x1, xp_timestamp(pl,t1),
x2, xp_timestamp(pl,t2));
break;
default:
fprintf(stderr, "CallDoPlot: Illegal number of arguments (%d)\n", plot_argc);
}
}
else {
switch(plot_argc) {
case 0:
snprintf(fmt, sizeof(fmt), "%s", plot_cmd);
DoPlot(pl, fmt);
break;
case 2:
snprintf(fmt, sizeof(fmt), "%s %%s %%u", plot_cmd);
DoPlot(pl, fmt,
xp_timestamp(pl,t1), x1);
break;
case 4:
snprintf(fmt, sizeof(fmt), "%s %%s %%u %%s %%u", plot_cmd);
DoPlot(pl, fmt,
xp_timestamp(pl,t1), x1,
xp_timestamp(pl,t2), x2);
break;
default:
fprintf(stderr, "CallDoPlot: Illegal number of arguments (%d)\n", plot_argc);
}
}
return;
}
static void
WritePlotHeader(
PLOTTER pl)
{
MFILE *f = NULL;
struct plotter_info *ppi;
if (pl == NO_PLOTTER)
return;
if (pl > plotter_ix) {
fprintf(stderr,"Illegal plotter: %d\n", pl);
exit(-1);
}
ppi = &pplotters[pl];
if ((f = ppi->fplot) == NULL)
return;
if(ppi->axis_switched) {
/* Header for the Time Line Charts */
Mfprintf(f,"%s %s\n", "unsigned", "dtime");
}
else {
/* Header for all other plots */
/* graph coordinates... */
/* X coord is timeval unless graph_time_zero is true */
/* Y is signed except when it's a sequence number */
/* ugly hack -- unsigned makes the graphs hard to work with and is
only needed for the time sequence graphs */
/* suggestion by Michele Clark at UNC - make them double instead */
Mfprintf(f,"%s %s\n",
graph_time_zero?"dtime":"timeval",
((strcmp(ppi->ylabel,"sequence number") == 0)&&(!graph_seq_zero))?
"double":"signed");
}
if (show_title) {
if (xplot_title_prefix)
Mfprintf(f,"title\n%s %s\n",
ExpandFormat(xplot_title_prefix),
ppi->title);
else
Mfprintf(f,"title\n%s\n", ppi->title);
}
Mfprintf(f,"xlabel\n%s\n", ppi->xlabel);
Mfprintf(f,"ylabel\n%s\n", ppi->ylabel);
/* Indicate that the header has now been written to the plotter file */
ppi->header_done = TRUE;
return;
}
/* Switch the x and y axis type (Needed for Time Line Charts. Default = FLASE) */
void plotter_switch_axis(
PLOTTER pl,
Bool flag)
{
struct plotter_info *ppi = &pplotters[pl];
ppi->axis_switched = flag;
}
syntax highlighted by Code2HTML, v. 0.9.1