/************************************************/ /* UtilHist_proc.c - Utility for extracting */ /* the process utilitizations from the */ /* EventHist.dat file to make a bar-chart */ /* histogram (with hyper-notes) plot-file. */ /* Hyper-notes are used to keep the process */ /* names, because there may not be enough room */ /* to write their names on a busy graph. */ /* */ /* Assumes you are in the directory where you */ /* ran a simulation. To use, run this */ /* utility. It reads your "netinfo" file to */ /* find the name-ID-numbers of your PE's, and */ /* then it reads your EventHist.dat file to */ /* produce a plot-file called, */ /* "process_utilz.dat". */ /* Then run XGRAPH on process_utilz.dat. */ /* Example: */ /* utilhist_proc */ /* xgraph process_utilz.dat */ /* */ /* To compile this program: */ /* gcc utilhist_proc.c -o utilhist_proc */ /* */ /* ("Hyper-notes" is a trademark of ATL-CSIM.) */ /************************************************/ #include #include FILE *infile, *outfile; /**********************************************/ /* Data structure to hold PE name-ID's. */ /**********************************************/ struct wordpair { char *name, *value; struct proctimes *processes; struct wordpair *nxt; } *nameIDs=0; /****************************************************/ /* Data structure to hold accumulated Process Times */ /****************************************************/ struct proctimes { char *procname; float accumulated_time; struct proctimes *nxt; }; /**********************************************/ /* Routine to get a new line from input file. */ /**********************************************/ void read_line( char *line ) { int i=0; do line[i++] = getc(infile); while ((!feof(infile)) && (line[i-1]!=10)); line[i-1] = '\0'; } /**************************************************/ /* Routine to add a name-value pair to word-list. */ /**************************************************/ void add_word( char *name, char *value ) { struct wordpair *tmppt; tmppt = (struct wordpair *)malloc(sizeof(struct wordpair)); tmppt->name = strdup(name); tmppt->value = strdup(value); tmppt->processes = 0; tmppt->nxt = nameIDs; nameIDs = tmppt; } /**************************************************/ /* Routine to update a named value. */ /**************************************************/ void update_entry( char *name, char *process_name, float T1, float T2 ) { struct wordpair *tmppt; struct proctimes *process, *tmpproc; tmppt = nameIDs; while ((tmppt!=0) && (strcmp(tmppt->name,name)!=0)) tmppt = tmppt->nxt; if (tmppt==0) printf("ERROR: Could not find device '%s'\n", name); else { process = tmppt->processes; while ((process!=0) && (strcmp(process->procname,process_name)!=0)) process = process->nxt; if (process==0) { tmpproc = (struct proctimes *)malloc(sizeof(struct proctimes)); tmpproc->nxt = tmppt->processes; tmppt->processes = tmpproc; tmpproc->procname = strdup(process_name); tmpproc->accumulated_time = T2 - T1; } else process->accumulated_time = process->accumulated_time + T2 - T1; } } /*....................................................................... . NEXT_WORD - accepts a line of text, and returns with the . . next word in that text in the second parameter, the original line . . is shortened from the beginning so that the word is removed. . . If the line encountered is empty, then the word returned will be . . empty. NEXTWORD parses on arbitrary delimiters. . .......................................................................*/ void Next_Word( char *line, char *word, char *delim ) { int i=0, j=0, k=0, m=0, flag=1; /* Eat away preceding garbage */ while ((line[i]!='\0') && (flag)) { j = 0; while ((delim[j]!='\0') && (line[i]!=delim[j])) j = j + 1; if (line[i]==delim[j]) { k = k + 1; i = i + 1; } else flag = 0; } /* Copy the word until the next delimiter. */ while ((line[i]!='\0') && (!flag)) { word[m] = line[i]; m = m + 1; i = i + 1; if (line[i]!='\0') { j = 0; while ((delim[j]!='\0') && (line[i]!=delim[j])) j = j + 1; if (line[i]==delim[j]) flag = 1; } } /* Shorten line. */ j = 0; while (line[i]!='\0') { line[j] = line[i]; j = j + 1; i = i + 1; } /* Terminate the char-strings. */ line[j] = '\0'; word[m] = '\0'; } /********************************************************/ /* Main - Open files, scan them, and produce output. */ /********************************************************/ main() { char line[1000], word[500], name[500], *ID; int flag=0, NPEs=0; float LastTime=0.0; /* First get the processor name - ID numbers. */ infile = fopen("netinfo","r"); if (infile == 0) {printf("ERROR: Cannot open netinfo.\n"); exit(0);} /* Scan the netinfo file for name-ID numbers. */ read_line( line ); while ((!feof(infile)) && (flag<2)) { Next_Word( line, word, " "); if (strcmp(word,"Sim>")==0) flag++; else if (strlen(word)>0) { Next_Word( line, name, " "); add_word( name, word ); NPEs++; } read_line( line ); } fclose(infile); /* Now scan EventHist.dat for processor utilizations. */ infile = fopen("EventHist.dat","r"); if (infile == 0) {printf("ERROR: Cannot open EventHist.dat.\n"); exit(0);} /* Open output file and write graph titles, etc.. */ outfile = fopen("process_utilz.dat","w"); if (infile == 0) {printf("ERROR: Cannot open process_utilz.dat for writing.\n"); exit(0);} /* Bar graph. Set bar thickness. */ fprintf(outfile,"thickness=%f\n", 25.0 / (1.0 + 0.1 * (float)NPEs) ); fprintf(outfile,"title = Process Utilizations\n"); fprintf(outfile,"title_x = Processor Element\n"); fprintf(outfile,"title_y = Process Utilization (%c)\n", 37); /*******************************************************/ /* Expect EventHist.dat to have the following format: */ /* PEname @ time : begin process_X */ /* Example: */ /* /pe2 @ 1700.5 : begin process_X */ /* /pe2 @ 3700.0 : end process_X */ /*******************************************************/ read_line( line ); while (!feof(infile)) { float T1, T2; char process_name[500]; if ((strstr(line,"begin")!=0) || (strstr(line,"end")!=0)) { Next_Word( line, name, " "); /* PE name */ Next_Word( line, word, " "); /* @ */ Next_Word( line, word, " "); /* time */ if (sscanf(word,"%f",&T2)!=1) printf("ERROR: Bad time '%s'\n", word); if (T2 > LastTime) LastTime = T2; Next_Word( line, word, " "); /* : */ Next_Word( line, word, " "); /* begin/end */ Next_Word( line, process_name, " "); /* process-name */ if (strcmp(word,"begin")==0) T1 = T2; else if (strcmp(word,"end")==0) update_entry( name, process_name, T1, T2 ); } read_line( line ); } fclose(infile); /* Now the EventHist.dat file is scanned. */ /* Now spew the results. */ { struct wordpair *tmppt; struct proctimes *tmpproc; float utilz; int color_wheel=2; tmppt = nameIDs; while (tmppt!=0) { utilz = 0.0; tmpproc = tmppt->processes; while (tmpproc!=0) { fprintf(outfile,"color=%d\n",color_wheel++); if (color_wheel>15) color_wheel = 2; /* Rotate color-wheel. */ fprintf(outfile,"%s %f\n", tmppt->value, utilz ); utilz = utilz + 100.0 * tmpproc->accumulated_time / LastTime; fprintf(outfile,"%s %f\n", tmppt->value, utilz ); fprintf(outfile,"next\n"); fprintf(outfile," %s %f Task = %s (%1.2f%c) \n", tmppt->value, utilz - 50.0 * tmpproc->accumulated_time / LastTime, tmpproc->procname, 100.0 * tmpproc->accumulated_time / LastTime, 37 ); tmpproc = tmpproc->nxt; } tmppt = tmppt->nxt; } } fclose(outfile); printf("\nTo plot Process-Utilizations:\n xgraph process_utilz.dat\n"); printf(" (Hint: Click on center of bars to see task-names.)\n\n"); }