📄 details.c
字号:
/* * Copyright (C) 1996-1998 by the Board of Trustees * of Leland Stanford Junior University. * * This file is part of the SimOS distribution. * See LICENSE file for terms of the license. * *//***************************************************************** * Tcl interface to detailed (by address or by PC) stat gathering *****************************************************************/#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <sys/fcntl.h>#include "tcl_init.h"#include "simtypes.h"#include "simutil.h"#include "statrecord.h"#include "sim_error.h"#include "syslimits.h"#include "string.h"#include "machine_params.h"#include <ctype.h>#include "simrecord.h"#include "../symbols/symbols.h"#include "visual.h"#define DEFAULT_COUNTER_SIZE 4 /* use ints */#define DEFAULT_HIGHRES SCACHE_LINE_SIZE /* cache line */#define DEFAULT_LOWRES (2048*1024) /* 2 megs */#define CHECK_DETAIL(detail, detailName) \ detailName = argv[2]; \ detail = DetailLookup(detailName); \ if (detail == NULL) { \ Tcl_AppendResult(interp, "no detail named \"", detailName, "\"", NULL); \ return TCL_ERROR; \ } typedef struct DetailCmd DetailCmd;typedef struct Detail Detail;struct Detail { char *name; StatRecordDetail *simosDetail;};static Detail *DetailLookup(char *name);static int cmdCreate(Tcl_Interp *interp, int argc, char *argv[]);static int cmdSet(Tcl_Interp *interp, int argc, char *argv[]);static int cmdDump(Tcl_Interp *interp, int argc, char *argv[]);static int cmdReport(Tcl_Interp *interp, int argc, char *argv[]);static int cmdGetData(Tcl_Interp *interp, int argc, char *argv[]);static int cmdForEach(Tcl_Interp *interp, int argc, char *argv[]);static int cmdGetFields(Tcl_Interp *interp, int argc, char *argv[]);static tclcmd detailCmds[] = {{ "create", 5, cmdCreate, " create detailName (byPc|byData) {options}"},{ "set", 5, cmdSet, " set detailName cpuNum bool"},{ "dump", -1, cmdDump, " dump detailName ?filename?"},{ "report", 4, cmdReport, " report detailName channelId"},{ "getData", -1, cmdGetData, " getdata detailName address ?fieldList?"},{ "foreach", 5, cmdForEach, " foreach detailName {rangeaddr rangeLen} {script}"},{ "getFields", 3, cmdGetFields, " getfields detailName"},{ NULL, 0, NULL, NULL}};static Tcl_HashTable details;/* My lame version of strcmp which just returns 0 if same, 1 if different. It's case independent*/static int casematch(char *s1, char *s2) { if (strlen(s1) != strlen(s2)) { return 0; } while (*s1) { if (toupper(*s1) != toupper(*s2)) { return 0; } s1++; s2++; } return 1;}void DetailInit(Tcl_Interp *interp) { Tcl_InitHashTable(&details, TCL_STRING_KEYS); Tcl_CreateCommand(interp, "detail", DispatchCmd, (ClientData) detailCmds, NULL);}int cmdCreate(Tcl_Interp *interp, int argc, char *argv[]) { Detail *detail; char *detailName; int i,j; Tcl_HashEntry *entry; int new; int listArgc; char **listArgv; int posCount; int isByPc; int optionArgc; char **optionArgv; int counterSize = DEFAULT_COUNTER_SIZE; int highresSize = DEFAULT_HIGHRES; int lowresSize = DEFAULT_LOWRES; if (argc != 4 && argc != 5) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], detailCmds[0].usage, "\"", NULL); return TCL_ERROR; } detailName = argv[2]; entry = Tcl_CreateHashEntry(&details, detailName, &new); if (!new) { Tcl_AppendResult(interp, "detail name already taken \"", detailName, "\"", NULL); return TCL_ERROR; } detail = (Detail *) ZMALLOC(sizeof(Detail),"TclDetail"); detail->name = Tcl_GetHashKey(&details, entry); if (casematch(argv[3], "byPc")) { isByPc = 1; } else if (casematch(argv[3], "byData")) { isByPc = 0; } else { Tcl_AppendResult(interp, "detail type \"", argv[3], "\"", " not supported", NULL); return TCL_ERROR; } /* Now look for the command options */ if (Tcl_SplitList(interp, argv[4], &listArgc, &listArgv) != TCL_OK) { Tcl_AppendResult(interp, "can't parse field list", NULL); return TCL_ERROR; } /* We go through the loop twice--first extracting the information needed to create the detail from C, and then extracting the info which translates to calls on the already created detail. */ for (i = 0; i < listArgc; i++) { if (Tcl_SplitList(interp, listArgv[i], &optionArgc, &optionArgv) != TCL_OK || optionArgc < 2) { Tcl_AppendResult(interp, "each option must be a list with >=2 elements", NULL); return TCL_ERROR; } if (casematch(optionArgv[0],"bucketSize")) { if (Tcl_GetInt(interp, optionArgv[1], &counterSize) != TCL_OK || (counterSize != 4 && counterSize != 8)) { Tcl_AppendResult(interp, "bucketSize for details must be 4 or 8", NULL); return TCL_ERROR; } continue; } if (casematch(optionArgv[0], "highResSize")) { if (Tcl_GetInt(interp, optionArgv[1], &highresSize) != TCL_OK) { Tcl_AppendResult(interp, "highResSize must be an integer", NULL); return TCL_ERROR; } continue; } if (casematch(optionArgv[0], "lowResSize")) { if (Tcl_GetInt(interp, optionArgv[1], &lowresSize) != TCL_OK) { Tcl_AppendResult(interp, "lowResSize must be an integer", NULL); return TCL_ERROR; } continue; } } /* Supply detailName to serve as the name of the detail and the name of the table. Note that Ed's old code used the output filename and detailName as one and the same. For now, I don't really change this; detailName is actually stored in the detail as its "filename." However, I've changed the SimRecordDump function to optionally take a different output filename to make it compatible with our model and the previous model too. */ detail->simosDetail = StatRecordNewDetail(detailName, isByPc, counterSize, lowresSize, highresSize); /* Now iterate again, this time making calls which initialize the detail */ posCount = 0; for (i = 0; i < listArgc; i++) { if (Tcl_SplitList(interp, listArgv[i], &optionArgc, &optionArgv) != TCL_OK || optionArgc < 2) { Tcl_AppendResult(interp, "each option must be a list with >=2 elements", NULL); return TCL_ERROR; } if (casematch(optionArgv[0], "field")) { if (optionArgc == 1) { Tcl_AppendResult(interp, "no field specified", NULL); return TCL_ERROR; } for (j = 1; j < optionArgc; j++) { StatRecordDetailAddField(detail->simosDetail, posCount, optionArgv[j]); } posCount++; continue; } if (casematch(optionArgv[0], "highResRange")) { VA addr; int len; if (optionArgc != 3) { Tcl_AppendResult(interp, "must specify range and length", NULL); return TCL_ERROR; } if (Tcl_GetInt(interp, optionArgv[1], (int *)&addr) != TCL_OK || Tcl_GetInt(interp, optionArgv[2], &len) != TCL_OK) { Tcl_AppendResult(interp, "\"", optionArgv[1], "\" or \"", optionArgv[2], "\" isn't a valid number", NULL); return TCL_ERROR; } StatRecordDetailHighResRange(detail->simosDetail, addr, len); continue; } } Tcl_SetHashValue(entry, detail); return TCL_OK;} int cmdDump(Tcl_Interp *interp, int argc, char *argv[]) { Detail *detail; char *detailName, *fileName, *fnamePtr; int fd; CHECK_DETAIL(detail, detailName); if (argc != 3 && argc != 4) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], detailCmds[2].usage, "\"", NULL); return TCL_ERROR; } if (!StatRecordDetailTableOK(detail->simosDetail)) { Tcl_AppendResult(interp, "data only available once simulator is running", NULL); return TCL_ERROR; } fileName = NULL; if (argc == 3) { fileName = (char*) ZMALLOC(strlen(detailName) + 5, "TclcmdDump"); strcpy(fileName, detailName); strcat(fileName, ".out"); fnamePtr = fileName; } else { fnamePtr = argv[3]; } fd = open(fnamePtr, O_RDWR|O_CREAT|O_TRUNC, 0666); ASSERT(fd >= 0); StatRecordDetailDump(detail->simosDetail, fd, 0); close(fd); return TCL_OK;}int cmdReport(Tcl_Interp *interp, int argc, char *argv[]) { Detail *detail; char *detailName, *fileName; int ret, sd; CHECK_DETAIL(detail, detailName); if (!StatRecordDetailTableOK(detail->simosDetail)) { Tcl_AppendResult(interp, "data only available once simulator is running", NULL); return TCL_ERROR; } sd = VisualGetStream(argv[3]); if (sd == 0) { Tcl_AppendResult(interp, "detail report: invalid stream identifier ", argv[3], NULL); return TCL_ERROR; } StatRecordDetailDump(detail->simosDetail, sd, 1); return TCL_OK;}int cmdSet(Tcl_Interp *interp, int argc, char *argv[]) { Detail *detail; char *detailName; int cpuNum; int bool; CHECK_DETAIL(detail, detailName); if (Tcl_GetInt(interp, argv[3], &cpuNum) != TCL_OK) { Tcl_AppendResult(interp, "bad cpunum \"", argv[3], "\"", NULL); return TCL_ERROR; } if (Tcl_GetBoolean(interp, argv[4], &bool) != TCL_OK) { Tcl_AppendResult(interp, "illegal boolean: \"", argv[4], "\"", NULL); return TCL_ERROR; } StatRecordDetailSetActive(detail->simosDetail, cpuNum, bool); return TCL_OK;}int cmdGetData(Tcl_Interp *interp, int argc, char *argv[]) { Detail *detail; char *detailName; VA addr; void *bucketPtr; int i; CHECK_DETAIL(detail, detailName); if (argc != 4 && argc != 5) { Tcl_AppendResult(interp, "wrong # of args: should be \"", argv[0], detailCmds[3].usage, "\"", NULL); return TCL_ERROR; } if (SymInt(interp, argv[3], (uint64 *)&addr) != TCL_OK) { return TCL_ERROR; } if (!StatRecordDetailTableOK(detail->simosDetail)) { Tcl_AppendResult(interp, "data only available once simulator is running", NULL); return TCL_ERROR; } bucketPtr = StatRecordDetailBucketAddr(detail->simosDetail, (VA)addr); if (argc == 4) { /* Return all counters for a given address, in a list */ for (i = 0; i < StatRecordDetailNumCounters(detail->simosDetail); i++) { char buf[50]; PrintLLD(buf, StatRecordDetailCounterByIndex(detail->simosDetail, bucketPtr, i)); Tcl_AppendElement(interp, buf); } } else { /* Go through the list of supplied fields, and extract only the ones requested. This is a bit complicated because multiple fields may belong to a single counter. Our solution: mainly we punt-- the programmer must be smart enough to know when a counter actually is the sum of multiple fields, because we just return the counter for any field. Example: Suppose f1, f2, and f3 all spew their stats into the same counter c. Then asking for fields f1, f2, f3 will return the list c, c, c. Get it? */ /* Process field list */ int listArgc; char **listArgv; int i; ASSERT(argc == 5); if (Tcl_SplitList(interp, argv[4], &listArgc, &listArgv) != TCL_OK) { Tcl_AppendResult(interp, "can't parse field list", NULL); return TCL_ERROR; } for (i = 0; i < listArgc; i++) { char buf[50]; PrintLLD(buf,StatRecordDetailCounterByField(detail->simosDetail, bucketPtr, listArgv[i])); Tcl_AppendElement(interp, buf); } free((char*) listArgv); } return TCL_OK;}int cmdGetFields(Tcl_Interp *interp, int argc, char *argv[]) { Detail *detail; char *detailName; int i,j; char tempString[2048]; char *fieldStr; CHECK_DETAIL(detail, detailName); /* "fields" returned by the detail can be either normal strings or NULL. NULL indicates that there are no more fields on the current position and we should check the next one. For some reason the first element is NULL.... */ tempString[0] = 0; j = StatRecordDetailNumFields(detail->simosDetail); for (i = 1; i < j; i++) { fieldStr = StatRecordDetailGetFieldName(detail->simosDetail, i); if (!fieldStr) { /* First position must have a field! */ ASSERT(i != 1); /* Get rid of trailing space */ ASSERT(strlen(tempString)); tempString[strlen(tempString)-1]=0; Tcl_AppendElement(interp, tempString); tempString[0] = 0; } else { /* Assuming the combined length of field names stored at the same position will be small. */ ASSERT(strlen(tempString) + strlen(fieldStr) + 2 < 2048); strcat(tempString, fieldStr); strcat(tempString, " "); } } return TCL_OK;} int cmdForEach(Tcl_Interp *interp, int argc, char *argv[]) {#if 0 Detail *detail; char *detailName; int listArgc; char **listArgv; /* don't bother with this thing for now... */ CHECK_DETAIL(detail, detailName); if (Tcl_SplitList(interp, argv[3], &listArgc, &listArgv) != TCL_OK || listArgc != 2) { Tcl_AppendResult(interp, "can't parse arg list", NULL); return TCL_ERROR; } /* Currently we don't create a new function. Scott what was the decision here? */ /* THis is a bit complicated, taken directly from SimRecDump. */ for (/*All ranges*/) { char addrString[50]; char lenString[50]; int addr, len; addr = /* addr */; len = /* len */; sprintf(addrString,"%i",addr); sprintf(lenString,"%i", len); /* Set the current value ... */ if (Tcl_SetVar(interp, listArgv[0], addrString ,0) == NULL || Tcl_SetVar(interp, listArgv[1], lenString, 0) == NULL) { Tcl_AppendResult(interp, "Error in range enumeration", NULL); return TCL_ERROR; } /* and evaluate! */ Tcl_Eval(interp, argv[4]); }#endif return TCL_OK;}Detail *DetailLookup(char *name) { Tcl_HashEntry *entry; entry = Tcl_FindHashEntry(&details, name); if (entry == NULL) { return NULL; } return (Detail*) Tcl_GetHashValue(entry);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -