diff options
| author | Nick Barnes | 2012-11-13 12:51:58 +0000 |
|---|---|---|
| committer | Nick Barnes | 2012-11-13 12:51:58 +0000 |
| commit | 686ee3810172f6c8a0a25042c64698adc048506d (patch) | |
| tree | 02621af982939a87073d7b1bd8a4f06d54699887 /mps/code | |
| parent | 1330a1a99cdc425d308cfa0f0f50d161bfe9f1e2 (diff) | |
| download | emacs-686ee3810172f6c8a0a25042c64698adc048506d.tar.gz emacs-686ee3810172f6c8a0a25042c64698adc048506d.zip | |
Tidy up the event pipeline.
Copied from Perforce
Change: 180460
ServerID: perforce.ravenbrook.com
Diffstat (limited to 'mps/code')
| -rw-r--r-- | mps/code/eventcnv.c | 62 | ||||
| -rw-r--r-- | mps/code/eventsql.c | 1029 | ||||
| -rw-r--r-- | mps/code/eventtxt.c | 534 | ||||
| -rw-r--r-- | mps/code/mps.xcodeproj/project.pbxproj | 87 | ||||
| -rw-r--r-- | mps/code/testlib.h | 24 |
5 files changed, 1197 insertions, 539 deletions
diff --git a/mps/code/eventcnv.c b/mps/code/eventcnv.c index 4ac57198b9c..99974831908 100644 --- a/mps/code/eventcnv.c +++ b/mps/code/eventcnv.c | |||
| @@ -2,22 +2,35 @@ | |||
| 2 | * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. | 2 | * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. |
| 3 | * | 3 | * |
| 4 | * This is a command-line tool that converts a binary format telemetry output | 4 | * This is a command-line tool that converts a binary format telemetry output |
| 5 | * stream from the MPS into a textual format. | 5 | * stream from the MPS into a more-portable textual format. |
| 6 | * | 6 | * |
| 7 | * The default MPS library will write a telemetry stream to a file called | 7 | * eventcnv can only read binary-format files that come from an MPS |
| 8 | * "mpsio.log" when the environment variable MPS_TELEMETRY_CONTROL is set | 8 | * compiled on the same platform, whereas the text-format files it |
| 9 | * to an integer whose bits select event kinds. For example: | 9 | * produces can be processed on any platform. |
| 10 | * | 10 | * |
| 11 | * MPS_TELEMETRY_CONTROL=7 amcss | 11 | * The default MPS library will write a telemetry stream to a file |
| 12 | * when the environment variable MPS_TELEMETRY_CONTROL is set to an | ||
| 13 | * integer whose bits select event kinds. For example: | ||
| 12 | * | 14 | * |
| 13 | * will run the amcss test program and emit a file with event kinds 0, 1, 2. | 15 | * MPS_TELEMETRY_CONTROL=7 amcss |
| 14 | * The file can then be converted into text format with a command like: | ||
| 15 | * | 16 | * |
| 16 | * eventcnv | sort | 17 | * will run the amcss test program and emit a telemetry file with |
| 18 | * event kinds 0, 1, 2. The file can then be converted into a sorted | ||
| 19 | * text format log with a command like: | ||
| 17 | * | 20 | * |
| 18 | * Note that the eventcnv program can only read streams that come from an | 21 | * eventcnv | sort > mps-events.txt |
| 19 | * MPS compiled on the same platform. | ||
| 20 | * | 22 | * |
| 23 | * These text-format files have one line per event, and can be | ||
| 24 | * manipulated by various programs systems in the usual Unix way. | ||
| 25 | * | ||
| 26 | * The binary telemetry filename can be specified with a -f | ||
| 27 | * command-line argument (use -f - to specify standard input). If no | ||
| 28 | * filename is specified on the command line, the environment variable | ||
| 29 | * MPS_TELEMETRY_FILENAME is consulted (this is the same environment | ||
| 30 | * variable used to specify the telemetry file to the MPS library). | ||
| 31 | * If the environment variable does not exist, the default filename of | ||
| 32 | * "mpsio.log" is used. | ||
| 33 | * | ||
| 21 | * $Id$ | 34 | * $Id$ |
| 22 | */ | 35 | */ |
| 23 | 36 | ||
| @@ -143,7 +156,7 @@ static char *parseArgs(int argc, char *argv[]) | |||
| 143 | 156 | ||
| 144 | static void printHex(ulongest_t val) | 157 | static void printHex(ulongest_t val) |
| 145 | { | 158 | { |
| 146 | printf(" %"PRIXLONGEST, (ulongest_t)val); | 159 | printf(" %"PRIXLONGEST, (ulongest_t)val); |
| 147 | } | 160 | } |
| 148 | 161 | ||
| 149 | #define printParamP(p) printHex((ulongest_t)p) | 162 | #define printParamP(p) printHex((ulongest_t)p) |
| @@ -154,7 +167,7 @@ static void printHex(ulongest_t val) | |||
| 154 | 167 | ||
| 155 | static void printParamD(double d) | 168 | static void printParamD(double d) |
| 156 | { | 169 | { |
| 157 | printf(" %.10G", d); | 170 | printf(" %.10G", d); |
| 158 | } | 171 | } |
| 159 | 172 | ||
| 160 | static void printParamS(const char *str) | 173 | static void printParamS(const char *str) |
| @@ -201,7 +214,7 @@ static void readLog(EventProc proc) | |||
| 201 | EVENT_VERSION_MEDIAN, | 214 | EVENT_VERSION_MEDIAN, |
| 202 | EVENT_VERSION_MINOR); | 215 | EVENT_VERSION_MINOR); |
| 203 | 216 | ||
| 204 | if (event->EventInit.f3 != EventCodeMAX) | 217 | if (event->EventInit.f3 > EventCodeMAX) |
| 205 | evwarn("Event log may contain unknown events with codes from %d to %d", | 218 | evwarn("Event log may contain unknown events with codes from %d to %d", |
| 206 | EventCodeMAX+1, event->EventInit.f3); | 219 | EventCodeMAX+1, event->EventInit.f3); |
| 207 | 220 | ||
| @@ -212,19 +225,19 @@ static void readLog(EventProc proc) | |||
| 212 | event->EventInit.f5, | 225 | event->EventInit.f5, |
| 213 | MPS_WORD_WIDTH); | 226 | MPS_WORD_WIDTH); |
| 214 | break; | 227 | break; |
| 215 | } | 228 | } |
| 216 | 229 | ||
| 217 | EVENT_CLOCK_PRINT(stdout, eventTime); | 230 | EVENT_CLOCK_PRINT(stdout, eventTime); |
| 218 | printf(" %4X", (unsigned)code); | 231 | printf(" %4X", (unsigned)code); |
| 219 | 232 | ||
| 220 | switch (code) { | 233 | switch (code) { |
| 221 | #define EVENT_PARAM_PRINT(name, index, sort, ident) \ | 234 | #define EVENT_PARAM_PRINT(name, index, sort, ident) \ |
| 222 | printParam##sort(event->name.f##index); | 235 | printParam##sort(event->name.f##index); |
| 223 | #define EVENT_PRINT(X, name, code, always, kind) \ | 236 | #define EVENT_PRINT(X, name, code, always, kind) \ |
| 224 | case code: \ | 237 | case code: \ |
| 225 | EVENT_##name##_PARAMS(EVENT_PARAM_PRINT, name) \ | 238 | EVENT_##name##_PARAMS(EVENT_PARAM_PRINT, name) \ |
| 226 | break; | 239 | break; |
| 227 | EVENT_LIST(EVENT_PRINT, X) | 240 | EVENT_LIST(EVENT_PRINT, X) |
| 228 | default: | 241 | default: |
| 229 | evwarn("Unknown event code %d", code); | 242 | evwarn("Unknown event code %d", code); |
| 230 | } | 243 | } |
| @@ -269,10 +282,9 @@ int main(int argc, char *argv[]) | |||
| 269 | 282 | ||
| 270 | filename = parseArgs(argc, argv); | 283 | filename = parseArgs(argc, argv); |
| 271 | if (!filename) { | 284 | if (!filename) { |
| 272 | filename = getenv(TELEMETRY_FILENAME_ENVAR | 285 | filename = getenv(TELEMETRY_FILENAME_ENVAR); |
| 273 | ); | 286 | if(!filename) |
| 274 | if(!filename) | 287 | filename = DEFAULT_TELEMETRY_FILENAME; |
| 275 | filename = DEFAULT_TELEMETRY_FILENAME; | ||
| 276 | } | 288 | } |
| 277 | 289 | ||
| 278 | if (strcmp(filename, "-") == 0) | 290 | if (strcmp(filename, "-") == 0) |
diff --git a/mps/code/eventsql.c b/mps/code/eventsql.c index b8ac5d6e4f5..ce6f64258b5 100644 --- a/mps/code/eventsql.c +++ b/mps/code/eventsql.c | |||
| @@ -7,29 +7,16 @@ | |||
| 7 | * This is a command-line tool that imports events from a text-format | 7 | * This is a command-line tool that imports events from a text-format |
| 8 | * MPS telemetry file into a SQLite database file. | 8 | * MPS telemetry file into a SQLite database file. |
| 9 | * | 9 | * |
| 10 | * The default MPS library will write a binary-format telemetry file. | 10 | * The default MPS library will write a binary-format telemetry file |
| 11 | * The binary-format file can be converted into a text-format file | 11 | * which can be converted into a text-format file using the eventcnv |
| 12 | * using the eventcnv program with the -SC -v options. For | 12 | * program (q.v.). |
| 13 | * binary-format compatibility, eventcnv has to be run on the same | ||
| 14 | * platform as the MPS. | ||
| 15 | * | ||
| 16 | * These ASCII text-format files have one line per event, and can be | ||
| 17 | * manipulated by various splendid systems in the usual Unix way. This | ||
| 18 | * eventsql program is one such. | ||
| 19 | * | ||
| 20 | * Note that eventsql can read streams that come from an MPS running | ||
| 21 | * on another platform. This is another reason why we use the | ||
| 22 | * intermediate text-format file, rather than reading the | ||
| 23 | * binary-format file directly: you can run the MPS and then eventcnv | ||
| 24 | * on the target platform, then optionally analyse the resulting text | ||
| 25 | * file on some other machine. | ||
| 26 | * | 13 | * |
| 27 | * Each event type gets its own table in the database. These tables | 14 | * Each event type gets its own table in the database. These tables |
| 28 | * are created from the definitions in eventdef.h if they don't | 15 | * are created from the definitions in eventdef.h if they don't |
| 29 | * already exist. Each event becomes a single row in the appropriate | 16 | * already exist. Each event becomes a single row in the appropriate |
| 30 | * table, which has a column for each event parameter, a time column | 17 | * table, which has a column for each event parameter, a time column |
| 31 | * for the event time field, and a log_serial column to identify the | 18 | * for the event time field, and a log_serial column to identify the |
| 32 | * log file. Because the database schema depends on the event | 19 | * source log file. Because the database schema depends on the event |
| 33 | * definitions in eventdef.h, eventsql has to be compiled using the | 20 | * definitions in eventdef.h, eventsql has to be compiled using the |
| 34 | * same event header files as those used to compile the MPS and | 21 | * same event header files as those used to compile the MPS and |
| 35 | * eventcnv which generated and processed the telemetry output. | 22 | * eventcnv which generated and processed the telemetry output. |
| @@ -48,13 +35,17 @@ | |||
| 48 | * | 35 | * |
| 49 | * -v (verbose): Increase verbosity. eventsql logs to stderr. By | 36 | * -v (verbose): Increase verbosity. eventsql logs to stderr. By |
| 50 | * default, it doesn't log much; it can be made more and more | 37 | * default, it doesn't log much; it can be made more and more |
| 51 | * loquacious by adding more -v switches. Given one or more -v | 38 | * loquacious by adding more -v switches. |
| 52 | * switches, it also writes 'ticks' (periods) to stdout, to show | 39 | * |
| 53 | * progress (one dot is 100k events). | 40 | * -p (progress): Show progress with a series of dots written to |
| 41 | * standard output (one dot per 100,000 events processed). Defaults | ||
| 42 | * on if -v specified, off otherwise. | ||
| 54 | * | 43 | * |
| 55 | * -t (test): Run unit tests on parts of eventsql. There aren't many | 44 | * -t (test): Run unit tests on parts of eventsql. There aren't many |
| 56 | * of these. TODO: write more unit tests. | 45 | * of these. TODO: write more unit tests. |
| 57 | * | 46 | * |
| 47 | * -d (delete): Delete the SQL file before importing. | ||
| 48 | * | ||
| 58 | * -f (force): Import the events to SQL even if the SQL database | 49 | * -f (force): Import the events to SQL even if the SQL database |
| 59 | * already includes a record of importing a matching log file. | 50 | * already includes a record of importing a matching log file. |
| 60 | * | 51 | * |
| @@ -62,14 +53,14 @@ | |||
| 62 | * to be recreated. Important if you change event types or kinds in | 53 | * to be recreated. Important if you change event types or kinds in |
| 63 | * eventdef.h. | 54 | * eventdef.h. |
| 64 | * | 55 | * |
| 65 | * -l <logfile>: Import events from the named logfile. Defaults to | 56 | * -i <logfile>: Import events from the named logfile. Defaults to |
| 66 | * stdin. If the specified file (matched by size and modtime) has | 57 | * standard input. If the specified file (matched by size and |
| 67 | * previously been imported to the same database, it will not be | 58 | * modtime) has previously been imported to the same database, it will |
| 68 | * imported again unless -f is specified. | 59 | * not be imported again unless -f is specified. |
| 69 | * | 60 | * |
| 70 | * -d <database>: Import events to the named database file. If not | 61 | * -o <database>: Import events to the named database file. If not |
| 71 | * specified, eventsql will use the MPS_EVENT_DATABASE environment | 62 | * specified, eventsql will use the MPS_TELEMETRY_DATABASE environment |
| 72 | * variable, and default to mpsevent.db. | 63 | * variable, and default to "mpsevent.db". |
| 73 | * | 64 | * |
| 74 | * $Id$ | 65 | * $Id$ |
| 75 | */ | 66 | */ |
| @@ -83,15 +74,16 @@ | |||
| 83 | #include <stdlib.h> | 74 | #include <stdlib.h> |
| 84 | #include <string.h> | 75 | #include <string.h> |
| 85 | #include <sqlite3.h> | 76 | #include <sqlite3.h> |
| 77 | #include <unistd.h> /* for getopt */ | ||
| 86 | #include <sys/stat.h> | 78 | #include <sys/stat.h> |
| 87 | 79 | ||
| 88 | #define DATABASE_NAME_ENVAR "MPS_EVENT_DATABASE" | 80 | #define DATABASE_NAME_ENVAR "MPS_TELEMETRY_DATABASE" |
| 89 | #define DEFAULT_DATABASE_NAME "mpsevent.db" | 81 | #define DEFAULT_DATABASE_NAME "mpsevent.db" |
| 90 | 82 | ||
| 91 | typedef sqlite3_int64 int64; | 83 | typedef sqlite3_int64 int64; |
| 92 | 84 | ||
| 93 | /* we output rows of dots. One dot per SMALL_TICK events, | 85 | /* At non-zero verbosity levels we output rows of dots. One dot per |
| 94 | * BIG_TICK dots per row. */ | 86 | * SMALL_TICK events, BIG_TICK dots per row. */ |
| 95 | 87 | ||
| 96 | #define SMALL_TICK 100000 | 88 | #define SMALL_TICK 100000 |
| 97 | #define BIG_TICK 50 | 89 | #define BIG_TICK 50 |
| @@ -110,112 +102,124 @@ unsigned int verbosity = 0; | |||
| 110 | 102 | ||
| 111 | static void vlog(unsigned int level, const char *format, va_list args) | 103 | static void vlog(unsigned int level, const char *format, va_list args) |
| 112 | { | 104 | { |
| 113 | if (level <= verbosity) { | 105 | if (level <= verbosity) { |
| 114 | fflush(stderr); /* sync */ | 106 | fflush(stderr); /* sync */ |
| 115 | fprintf(stderr, "log %d: ", level); | 107 | fprintf(stderr, "log %d: ", level); |
| 116 | vfprintf(stderr, format, args); | 108 | vfprintf(stderr, format, args); |
| 117 | fprintf(stderr, "\n"); | 109 | fprintf(stderr, "\n"); |
| 118 | } | 110 | } |
| 119 | } | 111 | } |
| 120 | 112 | ||
| 121 | static void log(unsigned int level, const char *format, ...) | 113 | static void log(unsigned int level, const char *format, ...) |
| 122 | { | 114 | { |
| 123 | va_list args; | 115 | va_list args; |
| 124 | va_start(args, format); | 116 | va_start(args, format); |
| 125 | vlog(level, format, args); | 117 | vlog(level, format, args); |
| 126 | va_end(args); | 118 | va_end(args); |
| 127 | } | 119 | } |
| 128 | 120 | ||
| 129 | static void error(const char *format, ...) | 121 | static void error(const char *format, ...) |
| 130 | { | 122 | { |
| 131 | va_list args; | 123 | va_list args; |
| 132 | fprintf(stderr, "Fatal error: "); | 124 | fprintf(stderr, "Fatal error: "); |
| 133 | va_start(args, format); | 125 | va_start(args, format); |
| 134 | vlog(LOG_ALWAYS, format, args); | 126 | vlog(LOG_ALWAYS, format, args); |
| 135 | va_end(args); | 127 | va_end(args); |
| 136 | exit(1); | 128 | exit(1); |
| 137 | } | 129 | } |
| 138 | 130 | ||
| 139 | static void sqlite_error(int res, sqlite3 *db, const char *format, ...) | 131 | static void sqlite_error(int res, sqlite3 *db, const char *format, ...) |
| 140 | { | 132 | { |
| 141 | va_list args; | 133 | va_list args; |
| 142 | log(LOG_ALWAYS, "Fatal SQL error %d", res); | 134 | log(LOG_ALWAYS, "Fatal SQL error %d", res); |
| 143 | va_start(args, format); | 135 | va_start(args, format); |
| 144 | vlog(LOG_ALWAYS, format, args); | 136 | vlog(LOG_ALWAYS, format, args); |
| 145 | va_end(args); | 137 | va_end(args); |
| 146 | log(LOG_ALWAYS, "SQLite message: %s\n", sqlite3_errmsg(db)); | 138 | log(LOG_ALWAYS, "SQLite message: %s\n", sqlite3_errmsg(db)); |
| 147 | exit(1); | 139 | exit(1); |
| 148 | } | 140 | } |
| 149 | 141 | ||
| 142 | /* global control variables set by command-line parameters. */ | ||
| 143 | |||
| 150 | static char *prog; /* program name */ | 144 | static char *prog; /* program name */ |
| 151 | static int rebuild = FALSE; | 145 | static int rebuild = FALSE; |
| 146 | static int deleteDatabase = FALSE; | ||
| 152 | static int runTests = FALSE; | 147 | static int runTests = FALSE; |
| 153 | static int force = FALSE; | 148 | static int force = FALSE; |
| 149 | static int progress = FALSE; | ||
| 154 | static char *databaseName = NULL; | 150 | static char *databaseName = NULL; |
| 155 | static char *logFileName = NULL; | 151 | static char *logFileName = NULL; |
| 156 | 152 | ||
| 157 | static void usage(void) | 153 | static void usage(void) |
| 158 | { | 154 | { |
| 159 | fprintf(stderr, | 155 | fprintf(stderr, |
| 160 | "Usage: %s [-rtf] [-l <logfile>] [-d <database>] [-v -v -v ...]\n", | 156 | "Usage: %s [-rfdvt] [-i <logfile>] [-o <database>]\n" |
| 157 | " -h (help) : this message.\n" | ||
| 158 | " -r (rebuild) : re-create glue tables.\n" | ||
| 159 | " -f (force) : ignore previous import of same logfile.\n" | ||
| 160 | " -d (delete) : delete and recreate database file.\n" | ||
| 161 | " -v (verbose) : increase logging to stderr.\n" | ||
| 162 | " -p (progress): show progress with dots to stdout.\n" | ||
| 163 | " -t (test) : run self-tests.\n" | ||
| 164 | " -i <logfile> : read logfile (defaults to stdin)\n" | ||
| 165 | " -o <database>: write database (defaults to\n" | ||
| 166 | " " | ||
| 167 | DATABASE_NAME_ENVAR " or " DEFAULT_DATABASE_NAME ").\n", | ||
| 161 | prog); | 168 | prog); |
| 162 | } | 169 | } |
| 163 | 170 | ||
| 164 | static void usageError(void) | 171 | static void usageError(void) |
| 165 | { | 172 | { |
| 166 | usage(); | 173 | usage(); |
| 167 | error("Bad usage"); | 174 | error("Bad usage"); |
| 168 | } | 175 | } |
| 169 | 176 | ||
| 170 | /* parseArgs -- parse command line arguments */ | 177 | /* parseArgs -- parse command line arguments */ |
| 171 | 178 | ||
| 172 | static void parseArgs(int argc, char *argv[]) | 179 | static void parseArgs(int argc, char *argv[]) |
| 173 | { | 180 | { |
| 174 | int i = 1; | 181 | int ch; |
| 175 | 182 | ||
| 176 | if (argc >= 1) | 183 | if (argc >= 1) |
| 177 | prog = argv[0]; | 184 | prog = argv[0]; |
| 178 | else | 185 | else |
| 179 | prog = "unknown"; | 186 | prog = "unknown"; |
| 180 | 187 | ||
| 181 | while (i < argc) { /* consider argument i */ | 188 | while((ch = getopt(argc, argv, "vrdfthpi:o:")) != -1) { |
| 182 | if (argv[i][0] == '-') { /* it's an option argument */ | 189 | switch (ch) { |
| 183 | switch (argv[i][1]) { | 190 | case 'v': /* verbosity */ |
| 184 | case 'v': /* verbosity */ | 191 | ++ verbosity; |
| 185 | ++ verbosity; | 192 | break; |
| 186 | break; | 193 | case 'p': /* progress */ |
| 187 | case 'r': /* rebuild */ | 194 | progress = TRUE; |
| 188 | rebuild = TRUE; | 195 | break; |
| 189 | break; | 196 | case 'r': /* rebuild */ |
| 190 | case 'f': /* force */ | 197 | rebuild = TRUE; |
| 191 | force = TRUE; | 198 | break; |
| 192 | break; | 199 | case 'd': /* rebuild */ |
| 193 | case 't': /* run tests */ | 200 | deleteDatabase = TRUE; |
| 194 | runTests = TRUE; | 201 | break; |
| 195 | break; | 202 | case 'f': /* force */ |
| 196 | case 'l': /* log file name */ | 203 | force = TRUE; |
| 197 | ++ i; | 204 | break; |
| 198 | if (i == argc) | 205 | case 't': /* run tests */ |
| 199 | usageError(); | 206 | runTests = TRUE; |
| 200 | else | 207 | break; |
| 201 | logFileName = argv[i]; | 208 | case 'i': /* input (log file) name */ |
| 202 | break; | 209 | logFileName = optarg; |
| 203 | case 'd': /* database file name */ | 210 | break; |
| 204 | ++ i; | 211 | case 'o': /* output (database file) name */ |
| 205 | if (i == argc) | 212 | databaseName = optarg; |
| 206 | usageError(); | 213 | break; |
| 207 | else | 214 | case '?': case 'h': /* help */ |
| 208 | databaseName = argv[i]; | 215 | usage(); |
| 209 | break; | 216 | break; |
| 210 | case '?': case 'h': /* help */ | 217 | default: |
| 211 | usage(); | 218 | usageError(); |
| 212 | break; | 219 | } |
| 213 | default: | ||
| 214 | usageError(); | ||
| 215 | } | ||
| 216 | } /* if option */ | ||
| 217 | ++ i; | ||
| 218 | } | 220 | } |
| 221 | if (verbosity > LOG_ALWAYS) | ||
| 222 | progress = TRUE; | ||
| 219 | } | 223 | } |
| 220 | 224 | ||
| 221 | /* openDatabase(p) opens the database file and returns a SQLite 3 | 225 | /* openDatabase(p) opens the database file and returns a SQLite 3 |
| @@ -223,35 +227,44 @@ static void parseArgs(int argc, char *argv[]) | |||
| 223 | 227 | ||
| 224 | static sqlite3 *openDatabase(void) | 228 | static sqlite3 *openDatabase(void) |
| 225 | { | 229 | { |
| 226 | sqlite3 *db; | 230 | sqlite3 *db; |
| 227 | int res; | 231 | int res; |
| 228 | 232 | ||
| 229 | if (!databaseName) { | 233 | if (!databaseName) { |
| 230 | databaseName = getenv(DATABASE_NAME_ENVAR); | 234 | databaseName = getenv(DATABASE_NAME_ENVAR); |
| 231 | if(!databaseName) | 235 | if(!databaseName) |
| 232 | databaseName = DEFAULT_DATABASE_NAME; | 236 | databaseName = DEFAULT_DATABASE_NAME; |
| 233 | } | 237 | } |
| 234 | res = sqlite3_open_v2(databaseName, | 238 | |
| 235 | &db, | 239 | if (deleteDatabase) { |
| 236 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, | 240 | res = remove(databaseName); |
| 237 | NULL); /* use default sqlite_vfs object */ | 241 | if (res) |
| 242 | log(LOG_ALWAYS, "Could not remove database file %s", databaseName); | ||
| 243 | else | ||
| 244 | log(LOG_OFTEN, "Removed database file %s", databaseName); | ||
| 245 | } | ||
| 246 | |||
| 247 | res = sqlite3_open_v2(databaseName, | ||
| 248 | &db, | ||
| 249 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, | ||
| 250 | NULL); /* use default sqlite_vfs object */ | ||
| 238 | 251 | ||
| 239 | if (res != SQLITE_OK) | 252 | if (res != SQLITE_OK) |
| 240 | sqlite_error(res, db, "Opening %s failed", databaseName); | 253 | sqlite_error(res, db, "Opening %s failed", databaseName); |
| 241 | 254 | ||
| 242 | log(LOG_OFTEN, "Writing to %s.",databaseName); | 255 | log(LOG_OFTEN, "Writing to %s.",databaseName); |
| 243 | 256 | ||
| 244 | return db; | 257 | return db; |
| 245 | } | 258 | } |
| 246 | 259 | ||
| 247 | /* closeDatabase(db) closes the database opened by openDatabase(). */ | 260 | /* closeDatabase(db) closes the database opened by openDatabase(). */ |
| 248 | 261 | ||
| 249 | static void closeDatabase(sqlite3 *db) | 262 | static void closeDatabase(sqlite3 *db) |
| 250 | { | 263 | { |
| 251 | int res = sqlite3_close(db); | 264 | int res = sqlite3_close(db); |
| 252 | if (res != SQLITE_OK) | 265 | if (res != SQLITE_OK) |
| 253 | sqlite_error(res, db, "Closing database failed"); | 266 | sqlite_error(res, db, "Closing database failed"); |
| 254 | log(LOG_SOMETIMES, "Closed %s.", databaseName); | 267 | log(LOG_SOMETIMES, "Closed %s.", databaseName); |
| 255 | } | 268 | } |
| 256 | 269 | ||
| 257 | /* Utility functions for SQLite statements. */ | 270 | /* Utility functions for SQLite statements. */ |
| @@ -259,40 +272,40 @@ static void closeDatabase(sqlite3 *db) | |||
| 259 | static sqlite3_stmt *prepareStatement(sqlite3 *db, | 272 | static sqlite3_stmt *prepareStatement(sqlite3 *db, |
| 260 | const char *sql) | 273 | const char *sql) |
| 261 | { | 274 | { |
| 262 | int res; | 275 | int res; |
| 263 | sqlite3_stmt *statement; | 276 | sqlite3_stmt *statement; |
| 264 | log(LOG_SELDOM, "Preparing statement %s", sql); | 277 | log(LOG_SELDOM, "Preparing statement %s", sql); |
| 265 | res = sqlite3_prepare_v2(db, sql, | 278 | res = sqlite3_prepare_v2(db, sql, |
| 266 | -1, /* prepare whole string as statement */ | 279 | -1, /* prepare whole string as statement */ |
| 267 | &statement, | 280 | &statement, |
| 268 | NULL); | 281 | NULL); |
| 269 | if (res != SQLITE_OK) | 282 | if (res != SQLITE_OK) |
| 270 | sqlite_error(res, db, "statement preparation failed: %s", sql); | 283 | sqlite_error(res, db, "statement preparation failed: %s", sql); |
| 271 | return statement; | 284 | return statement; |
| 272 | } | 285 | } |
| 273 | 286 | ||
| 274 | static void finalizeStatement(sqlite3 *db, | 287 | static void finalizeStatement(sqlite3 *db, |
| 275 | sqlite3_stmt *statement) | 288 | sqlite3_stmt *statement) |
| 276 | { | 289 | { |
| 277 | int res; | 290 | int res; |
| 278 | res = sqlite3_finalize(statement); | 291 | res = sqlite3_finalize(statement); |
| 279 | if (res != SQLITE_OK) | 292 | if (res != SQLITE_OK) |
| 280 | sqlite_error(res, db, "statement finalize failed"); | 293 | sqlite_error(res, db, "statement finalize failed"); |
| 281 | } | 294 | } |
| 282 | 295 | ||
| 283 | static void runStatement(sqlite3 *db, | 296 | static void runStatement(sqlite3 *db, |
| 284 | const char *sql, | 297 | const char *sql, |
| 285 | const char *description) | 298 | const char *description) |
| 286 | { | 299 | { |
| 287 | int res; | 300 | int res; |
| 288 | log(LOG_SELDOM, "%s: %s", description, sql); | 301 | log(LOG_SELDOM, "%s: %s", description, sql); |
| 289 | res = sqlite3_exec(db, | 302 | res = sqlite3_exec(db, |
| 290 | sql, | 303 | sql, |
| 291 | NULL, /* No callback */ | 304 | NULL, /* No callback */ |
| 292 | NULL, /* No callback closure */ | 305 | NULL, /* No callback closure */ |
| 293 | NULL); /* error messages handled by sqlite_error */ | 306 | NULL); /* error messages handled by sqlite_error */ |
| 294 | if (res != SQLITE_OK) | 307 | if (res != SQLITE_OK) |
| 295 | sqlite_error(res, db, "%s failed - statement %s", description, sql); | 308 | sqlite_error(res, db, "%s failed - statement %s", description, sql); |
| 296 | } | 309 | } |
| 297 | 310 | ||
| 298 | /* Test for the existence of a table using sqlite_master table. | 311 | /* Test for the existence of a table using sqlite_master table. |
| @@ -300,60 +313,60 @@ static void runStatement(sqlite3 *db, | |||
| 300 | 313 | ||
| 301 | static int tableExists(sqlite3* db, const char *tableName) | 314 | static int tableExists(sqlite3* db, const char *tableName) |
| 302 | { | 315 | { |
| 303 | int res; | 316 | int res; |
| 304 | int exists; | 317 | int exists; |
| 305 | sqlite3_stmt *statement; | 318 | sqlite3_stmt *statement; |
| 306 | 319 | ||
| 307 | statement = prepareStatement(db, | 320 | statement = prepareStatement(db, |
| 308 | "SELECT 1 FROM sqlite_master WHERE type='table' AND name=?"); | 321 | "SELECT 1 FROM sqlite_master WHERE type='table' AND name=?"); |
| 309 | res = sqlite3_bind_text(statement, 1, tableName, -1, SQLITE_STATIC); | 322 | res = sqlite3_bind_text(statement, 1, tableName, -1, SQLITE_STATIC); |
| 310 | if (res != SQLITE_OK) | 323 | if (res != SQLITE_OK) |
| 311 | sqlite_error(res, db, "table existence bind of name failed."); | 324 | sqlite_error(res, db, "table existence bind of name failed."); |
| 312 | res = sqlite3_step(statement); | 325 | res = sqlite3_step(statement); |
| 313 | switch(res) { | 326 | switch(res) { |
| 314 | case SQLITE_DONE: | 327 | case SQLITE_DONE: |
| 315 | exists = 0; | 328 | exists = 0; |
| 316 | break; | 329 | break; |
| 317 | case SQLITE_ROW: | 330 | case SQLITE_ROW: |
| 318 | exists = 1; | 331 | exists = 1; |
| 319 | break; | 332 | break; |
| 320 | default: | 333 | default: |
| 321 | sqlite_error(res, db, "select from sqlite_master failed."); | 334 | sqlite_error(res, db, "select from sqlite_master failed."); |
| 322 | } | 335 | } |
| 323 | finalizeStatement(db, statement); | 336 | finalizeStatement(db, statement); |
| 324 | return exists; | 337 | return exists; |
| 325 | } | 338 | } |
| 326 | 339 | ||
| 327 | /* Unit test for tableExists() */ | 340 | /* Unit test for tableExists() */ |
| 328 | 341 | ||
| 329 | static struct { | 342 | static struct { |
| 330 | const char* name; | 343 | const char* name; |
| 331 | int exists; | 344 | int exists; |
| 332 | } tableTests[] = { | 345 | } tableTests[] = { |
| 333 | {"event_kind", TRUE}, | 346 | {"event_kind", TRUE}, |
| 334 | {"spong", FALSE}, | 347 | {"spong", FALSE}, |
| 335 | {"EVENT_SegSplit", TRUE} | 348 | {"EVENT_SegSplit", TRUE} |
| 336 | }; | 349 | }; |
| 337 | 350 | ||
| 338 | static void testTableExists(sqlite3 *db) | 351 | static void testTableExists(sqlite3 *db) |
| 339 | { | 352 | { |
| 340 | size_t i; | 353 | size_t i; |
| 341 | int defects = 0; | 354 | int defects = 0; |
| 342 | int tests = 0; | 355 | int tests = 0; |
| 343 | for (i=0; i < (sizeof(tableTests)/sizeof(tableTests[0])); ++i) { | 356 | for (i=0; i < (sizeof(tableTests)/sizeof(tableTests[0])); ++i) { |
| 344 | const char *name = tableTests[i].name; | 357 | const char *name = tableTests[i].name; |
| 345 | int exists = tableExists(db, name); | 358 | int exists = tableExists(db, name); |
| 346 | if (exists) | 359 | if (exists) |
| 347 | log(LOG_OFTEN, "Table exists: %s", name); | 360 | log(LOG_OFTEN, "Table exists: %s", name); |
| 348 | else | 361 | else |
| 349 | log(LOG_OFTEN, "Table does not exist: %s", name); | 362 | log(LOG_OFTEN, "Table does not exist: %s", name); |
| 350 | if (exists != tableTests[i].exists) { | 363 | if (exists != tableTests[i].exists) { |
| 351 | log(LOG_ALWAYS, "tableExists test failed on table %s", name); | 364 | log(LOG_ALWAYS, "tableExists test failed on table %s", name); |
| 352 | ++ defects; | 365 | ++ defects; |
| 353 | } | 366 | } |
| 354 | ++ tests; | 367 | ++ tests; |
| 355 | } | 368 | } |
| 356 | log(LOG_ALWAYS, "%d tests, %d defects found.", tests, defects); | 369 | log(LOG_ALWAYS, "%d tests, %d defects found.", tests, defects); |
| 357 | } | 370 | } |
| 358 | 371 | ||
| 359 | /* Every time we put events from a log file into a database file, we | 372 | /* Every time we put events from a log file into a database file, we |
| @@ -365,7 +378,7 @@ static void testTableExists(sqlite3 *db) | |||
| 365 | * | 378 | * |
| 366 | * When reading events from stdin, we can't so easily avoid the | 379 | * When reading events from stdin, we can't so easily avoid the |
| 367 | * duplication (unless we, e.g., take a hash of the event set); we | 380 | * duplication (unless we, e.g., take a hash of the event set); we |
| 368 | * assume that the user is smart enough not to do that. | 381 | * have to assume that the user is smart enough not to do that. |
| 369 | */ | 382 | */ |
| 370 | 383 | ||
| 371 | static int64 logSerial = 0; | 384 | static int64 logSerial = 0; |
| @@ -373,97 +386,97 @@ static int64 logSerial = 0; | |||
| 373 | static void registerLogFile(sqlite3 *db, | 386 | static void registerLogFile(sqlite3 *db, |
| 374 | const char *filename) | 387 | const char *filename) |
| 375 | { | 388 | { |
| 376 | sqlite3_stmt *statement; | 389 | sqlite3_stmt *statement; |
| 377 | int res; | 390 | int res; |
| 378 | const unsigned char *name; | 391 | const unsigned char *name; |
| 379 | int64 completed; | 392 | int64 completed; |
| 380 | int64 file_size; | 393 | int64 file_size; |
| 381 | int64 file_modtime; | 394 | int64 file_modtime; |
| 382 | 395 | ||
| 383 | if (filename) { | 396 | if (filename) { |
| 384 | struct stat st; | 397 | struct stat st; |
| 385 | res = stat(filename, &st); | 398 | res = stat(filename, &st); |
| 386 | if (res != 0) | 399 | if (res != 0) |
| 387 | error("Couldn't stat() %s", filename); | 400 | error("Couldn't stat() %s", filename); |
| 388 | file_size = st.st_size; | 401 | file_size = st.st_size; |
| 389 | file_modtime = st.st_mtime; | 402 | file_modtime = st.st_mtime; |
| 390 | 403 | ||
| 391 | statement = prepareStatement(db, | 404 | statement = prepareStatement(db, |
| 392 | "SELECT name, serial, completed FROM event_log" | 405 | "SELECT name, serial, completed FROM event_log" |
| 393 | " WHERE size = ? AND modtime = ?"); | 406 | " WHERE size = ? AND modtime = ?"); |
| 394 | res = sqlite3_bind_int64(statement, 1, file_size); | 407 | res = sqlite3_bind_int64(statement, 1, file_size); |
| 395 | if (res != SQLITE_OK) | 408 | if (res != SQLITE_OK) |
| 396 | sqlite_error(res, db, "event_log bind of size failed."); | 409 | sqlite_error(res, db, "event_log bind of size failed."); |
| 397 | res = sqlite3_bind_int64(statement, 2, file_modtime); | 410 | res = sqlite3_bind_int64(statement, 2, file_modtime); |
| 398 | if (res != SQLITE_OK) | 411 | if (res != SQLITE_OK) |
| 399 | sqlite_error(res, db, "event_log bind of modtime failed."); | 412 | sqlite_error(res, db, "event_log bind of modtime failed."); |
| 400 | res = sqlite3_step(statement); | 413 | res = sqlite3_step(statement); |
| 401 | switch(res) { | 414 | switch(res) { |
| 402 | case SQLITE_DONE: | 415 | case SQLITE_DONE: |
| 403 | log(LOG_SOMETIMES, "No log file matching '%s' found in database.", filename); | 416 | log(LOG_SOMETIMES, "No log file matching '%s' found in database.", filename); |
| 404 | break; | 417 | break; |
| 405 | case SQLITE_ROW: | 418 | case SQLITE_ROW: |
| 406 | name = sqlite3_column_text(statement, 0); | 419 | name = sqlite3_column_text(statement, 0); |
| 407 | logSerial = sqlite3_column_int64(statement, 1); | 420 | logSerial = sqlite3_column_int64(statement, 1); |
| 408 | completed = sqlite3_column_int64(statement, 2); | 421 | completed = sqlite3_column_int64(statement, 2); |
| 409 | log(force ? LOG_OFTEN : LOG_ALWAYS, "Log file matching '%s' already in event_log, named \"%s\" (serial %lu, completed %lu).", | 422 | log(force ? LOG_OFTEN : LOG_ALWAYS, "Log file matching '%s' already in event_log, named \"%s\" (serial %lu, completed %lu).", |
| 410 | filename, name, logSerial, completed); | 423 | filename, name, logSerial, completed); |
| 411 | if (force) { | 424 | if (force) { |
| 412 | log(LOG_OFTEN, "Continuing anyway because -f specified."); | 425 | log(LOG_OFTEN, "Continuing anyway because -f specified."); |
| 413 | } else { | 426 | } else { |
| 414 | log(LOG_ALWAYS, "Exiting. Specify -f to force events into SQL anyway."); | 427 | log(LOG_ALWAYS, "Exiting. Specify -f to force events into SQL anyway."); |
| 415 | exit(0); | 428 | exit(0); |
| 416 | } | 429 | } |
| 417 | break; | 430 | break; |
| 418 | default: | 431 | default: |
| 419 | sqlite_error(res, db, "select from event_log failed."); | 432 | sqlite_error(res, db, "select from event_log failed."); |
| 420 | } | 433 | } |
| 421 | finalizeStatement(db, statement); | 434 | finalizeStatement(db, statement); |
| 422 | } else { /* stdin */ | 435 | } else { /* stdin */ |
| 423 | filename = "<stdin>"; | 436 | filename = "<stdin>"; |
| 424 | file_size = 0; | 437 | file_size = 0; |
| 425 | file_modtime = 0; | 438 | file_modtime = 0; |
| 426 | } | 439 | } |
| 427 | statement = prepareStatement(db, | 440 | statement = prepareStatement(db, |
| 428 | "INSERT into event_log (name, size, modtime, completed)" | 441 | "INSERT into event_log (name, size, modtime, completed)" |
| 429 | " VALUES (?, ?, ?, 0)"); | 442 | " VALUES (?, ?, ?, 0)"); |
| 430 | res = sqlite3_bind_text(statement, 1, filename, -1, SQLITE_STATIC); | 443 | res = sqlite3_bind_text(statement, 1, filename, -1, SQLITE_STATIC); |
| 431 | if (res != SQLITE_OK) | 444 | if (res != SQLITE_OK) |
| 432 | sqlite_error(res, db, "event_log insert bind of name failed."); | 445 | sqlite_error(res, db, "event_log insert bind of name failed."); |
| 433 | res = sqlite3_bind_int64(statement, 2, file_size); | 446 | res = sqlite3_bind_int64(statement, 2, file_size); |
| 434 | if (res != SQLITE_OK) | 447 | if (res != SQLITE_OK) |
| 435 | sqlite_error(res, db, "event_log insert bind of size failed."); | 448 | sqlite_error(res, db, "event_log insert bind of size failed."); |
| 436 | res = sqlite3_bind_int64(statement, 3, file_modtime); | 449 | res = sqlite3_bind_int64(statement, 3, file_modtime); |
| 437 | if (res != SQLITE_OK) | 450 | if (res != SQLITE_OK) |
| 438 | sqlite_error(res, db, "event_log insert bind of modtime failed."); | 451 | sqlite_error(res, db, "event_log insert bind of modtime failed."); |
| 439 | res = sqlite3_step(statement); | 452 | res = sqlite3_step(statement); |
| 440 | if (res != SQLITE_DONE) | 453 | if (res != SQLITE_DONE) |
| 441 | sqlite_error(res, db, "insert into event_log failed."); | 454 | sqlite_error(res, db, "insert into event_log failed."); |
| 442 | logSerial = sqlite3_last_insert_rowid(db); | 455 | logSerial = sqlite3_last_insert_rowid(db); |
| 443 | log(LOG_SOMETIMES, "Log file %s added to event_log with serial %lu", | 456 | log(LOG_SOMETIMES, "Log file %s added to event_log with serial %lu", |
| 444 | filename, logSerial); | 457 | filename, logSerial); |
| 445 | finalizeStatement(db, statement); | 458 | finalizeStatement(db, statement); |
| 446 | } | 459 | } |
| 447 | 460 | ||
| 448 | static void logFileCompleted(sqlite3 *db, | 461 | static void logFileCompleted(sqlite3 *db, |
| 449 | int64 completed) | 462 | int64 completed) |
| 450 | { | 463 | { |
| 451 | sqlite3_stmt *statement; | 464 | sqlite3_stmt *statement; |
| 452 | int res; | 465 | int res; |
| 453 | 466 | ||
| 454 | statement = prepareStatement(db, | 467 | statement = prepareStatement(db, |
| 455 | "UPDATE event_log SET completed=? WHERE serial=?"); | 468 | "UPDATE event_log SET completed=? WHERE serial=?"); |
| 456 | res = sqlite3_bind_int64(statement, 2, logSerial); | 469 | res = sqlite3_bind_int64(statement, 2, logSerial); |
| 457 | if (res != SQLITE_OK) | 470 | if (res != SQLITE_OK) |
| 458 | sqlite_error(res, db, "event_log update bind of serial failed."); | 471 | sqlite_error(res, db, "event_log update bind of serial failed."); |
| 459 | res = sqlite3_bind_int64(statement, 1, completed); | 472 | res = sqlite3_bind_int64(statement, 1, completed); |
| 460 | if (res != SQLITE_OK) | 473 | if (res != SQLITE_OK) |
| 461 | sqlite_error(res, db, "event_log update bind of completed failed."); | 474 | sqlite_error(res, db, "event_log update bind of completed failed."); |
| 462 | res = sqlite3_step(statement); | 475 | res = sqlite3_step(statement); |
| 463 | if (res != SQLITE_DONE) | 476 | if (res != SQLITE_DONE) |
| 464 | sqlite_error(res, db, "insert into event_log failed."); | 477 | sqlite_error(res, db, "insert into event_log failed."); |
| 465 | log(LOG_SOMETIMES, "Marked in event_log: %lu events", completed); | 478 | log(LOG_SOMETIMES, "Marked in event_log: %lu events", completed); |
| 466 | finalizeStatement(db, statement); | 479 | finalizeStatement(db, statement); |
| 467 | } | 480 | } |
| 468 | 481 | ||
| 469 | /* Macro magic to make a CREATE TABLE statement for each event type. */ | 482 | /* Macro magic to make a CREATE TABLE statement for each event type. */ |
| @@ -488,67 +501,67 @@ static void logFileCompleted(sqlite3 *db, | |||
| 488 | /* An array of table-creation statement strings. */ | 501 | /* An array of table-creation statement strings. */ |
| 489 | 502 | ||
| 490 | const char *createStatements[] = { | 503 | const char *createStatements[] = { |
| 491 | "CREATE TABLE IF NOT EXISTS event_kind (name TEXT," | 504 | "CREATE TABLE IF NOT EXISTS event_kind (name TEXT," |
| 492 | " description TEXT," | 505 | " description TEXT," |
| 493 | " enum INTEGER PRIMARY KEY)", | 506 | " enum INTEGER PRIMARY KEY)", |
| 494 | 507 | ||
| 495 | "CREATE TABLE IF NOT EXISTS event_type (name TEXT," | 508 | "CREATE TABLE IF NOT EXISTS event_type (name TEXT," |
| 496 | " code INTEGER PRIMARY KEY," | 509 | " code INTEGER PRIMARY KEY," |
| 497 | " always INTEGER," | 510 | " always INTEGER," |
| 498 | " kind INTEGER," | 511 | " kind INTEGER," |
| 499 | " FOREIGN KEY (kind) REFERENCES event_kind(enum));", | 512 | " FOREIGN KEY (kind) REFERENCES event_kind(enum));", |
| 500 | 513 | ||
| 501 | "CREATE TABLE IF NOT EXISTS event_param (type INTEGER," | 514 | "CREATE TABLE IF NOT EXISTS event_param (type INTEGER," |
| 502 | " param_index INTEGER," | 515 | " param_index INTEGER," |
| 503 | " sort TEXT," | 516 | " sort TEXT," |
| 504 | " ident TEXT," | 517 | " ident TEXT," |
| 505 | " FOREIGN KEY (type) REFERENCES event_type(code));", | 518 | " FOREIGN KEY (type) REFERENCES event_type(code));", |
| 506 | 519 | ||
| 507 | "CREATE TABLE IF NOT EXISTS event_log (name TEXT," | 520 | "CREATE TABLE IF NOT EXISTS event_log (name TEXT," |
| 508 | " size INTEGER," | 521 | " size INTEGER," |
| 509 | " modtime INTEGER," | 522 | " modtime INTEGER," |
| 510 | " completed INTEGER," | 523 | " completed INTEGER," |
| 511 | " serial INTEGER PRIMARY KEY AUTOINCREMENT)", | 524 | " serial INTEGER PRIMARY KEY AUTOINCREMENT)", |
| 512 | 525 | ||
| 513 | EVENT_LIST(EVENT_TABLE_CREATE, X) | 526 | EVENT_LIST(EVENT_TABLE_CREATE, X) |
| 514 | }; | 527 | }; |
| 515 | 528 | ||
| 516 | /* makeTables makes all the tables. */ | 529 | /* makeTables makes all the tables. */ |
| 517 | 530 | ||
| 518 | static void makeTables(sqlite3 *db) | 531 | static void makeTables(sqlite3 *db) |
| 519 | { | 532 | { |
| 520 | size_t i; | 533 | size_t i; |
| 521 | log(LOG_SOMETIMES, "Creating tables."); | 534 | log(LOG_SOMETIMES, "Creating tables."); |
| 522 | 535 | ||
| 523 | for (i=0; i < (sizeof(createStatements)/sizeof(createStatements[0])); ++i) { | 536 | for (i=0; i < (sizeof(createStatements)/sizeof(createStatements[0])); ++i) { |
| 524 | runStatement(db, createStatements[i], "Table creation"); | 537 | runStatement(db, createStatements[i], "Table creation"); |
| 525 | } | 538 | } |
| 526 | } | 539 | } |
| 527 | 540 | ||
| 528 | const char *glueTables[] = { | 541 | const char *glueTables[] = { |
| 529 | "event_kind", | 542 | "event_kind", |
| 530 | "event_type", | 543 | "event_type", |
| 531 | "event_param", | 544 | "event_param", |
| 532 | }; | 545 | }; |
| 533 | 546 | ||
| 534 | static void dropGlueTables(sqlite3 *db) | 547 | static void dropGlueTables(sqlite3 *db) |
| 535 | { | 548 | { |
| 536 | size_t i; | 549 | size_t i; |
| 537 | int res; | 550 | int res; |
| 538 | char sql[1024]; | 551 | char sql[1024]; |
| 539 | 552 | ||
| 540 | log(LOG_ALWAYS, "Dropping glue tables so they are rebuilt."); | 553 | log(LOG_ALWAYS, "Dropping glue tables so they are rebuilt."); |
| 541 | 554 | ||
| 542 | for (i=0; i < (sizeof(glueTables)/sizeof(glueTables[0])); ++i) { | 555 | for (i=0; i < (sizeof(glueTables)/sizeof(glueTables[0])); ++i) { |
| 543 | log(LOG_SOMETIMES, "Dropping table %s", glueTables[i]); | 556 | log(LOG_SOMETIMES, "Dropping table %s", glueTables[i]); |
| 544 | sprintf(sql, "DROP TABLE %s", glueTables[i]); | 557 | sprintf(sql, "DROP TABLE %s", glueTables[i]); |
| 545 | res = sqlite3_exec(db, | 558 | res = sqlite3_exec(db, |
| 546 | sql, | 559 | sql, |
| 547 | NULL, /* No callback */ | 560 | NULL, /* No callback */ |
| 548 | NULL, /* No callback closure */ | 561 | NULL, /* No callback closure */ |
| 549 | NULL); /* error messages handled by sqlite_error */ | 562 | NULL); /* error messages handled by sqlite_error */ |
| 550 | /* Don't check for errors. */ | 563 | /* Don't check for errors. */ |
| 551 | } | 564 | } |
| 552 | } | 565 | } |
| 553 | 566 | ||
| 554 | /* Populate the metadata "glue" tables event_kind, event_type, and | 567 | /* Populate the metadata "glue" tables event_kind, event_type, and |
| @@ -623,32 +636,32 @@ static void dropGlueTables(sqlite3 *db) | |||
| 623 | 636 | ||
| 624 | static void fillGlueTables(sqlite3 *db) | 637 | static void fillGlueTables(sqlite3 *db) |
| 625 | { | 638 | { |
| 626 | int i; | 639 | int i; |
| 627 | sqlite3_stmt *statement; | 640 | sqlite3_stmt *statement; |
| 628 | int res; | 641 | int res; |
| 629 | 642 | ||
| 630 | statement = prepareStatement(db, | 643 | statement = prepareStatement(db, |
| 631 | "INSERT OR IGNORE INTO event_kind (name, description, enum)" | 644 | "INSERT OR IGNORE INTO event_kind (name, description, enum)" |
| 632 | "VALUES (?, ?, ?)"); | 645 | "VALUES (?, ?, ?)"); |
| 633 | 646 | ||
| 634 | i = 0; | 647 | i = 0; |
| 635 | EventKindENUM(EVENT_KIND_DO_INSERT, X); | 648 | EventKindENUM(EVENT_KIND_DO_INSERT, X); |
| 636 | 649 | ||
| 637 | finalizeStatement(db, statement); | 650 | finalizeStatement(db, statement); |
| 638 | 651 | ||
| 639 | statement = prepareStatement(db, | 652 | statement = prepareStatement(db, |
| 640 | "INSERT OR IGNORE INTO event_type (name, code, always, kind)" | 653 | "INSERT OR IGNORE INTO event_type (name, code, always, kind)" |
| 641 | "VALUES (?, ?, ?, ?)"); | 654 | "VALUES (?, ?, ?, ?)"); |
| 642 | EVENT_LIST(EVENT_TYPE_DO_INSERT, X); | 655 | EVENT_LIST(EVENT_TYPE_DO_INSERT, X); |
| 643 | 656 | ||
| 644 | finalizeStatement(db, statement); | 657 | finalizeStatement(db, statement); |
| 645 | 658 | ||
| 646 | statement = prepareStatement(db, | 659 | statement = prepareStatement(db, |
| 647 | "INSERT OR IGNORE INTO event_param (type, param_index, sort, ident)" | 660 | "INSERT OR IGNORE INTO event_param (type, param_index, sort, ident)" |
| 648 | "VALUES (?, ?, ?, ?)"); | 661 | "VALUES (?, ?, ?, ?)"); |
| 649 | EVENT_LIST(EVENT_TYPE_INSERT_PARAMS, X); | 662 | EVENT_LIST(EVENT_TYPE_INSERT_PARAMS, X); |
| 650 | 663 | ||
| 651 | finalizeStatement(db, statement); | 664 | finalizeStatement(db, statement); |
| 652 | } | 665 | } |
| 653 | 666 | ||
| 654 | /* Populate the actual event tables. */ | 667 | /* Populate the actual event tables. */ |
| @@ -693,219 +706,227 @@ static void fillGlueTables(sqlite3 *db) | |||
| 693 | 706 | ||
| 694 | static char *bind_int(sqlite3 *db, sqlite3_stmt *stmt, int64 count, int index, char *p) | 707 | static char *bind_int(sqlite3 *db, sqlite3_stmt *stmt, int64 count, int index, char *p) |
| 695 | { | 708 | { |
| 696 | char *q; | 709 | char *q; |
| 697 | long long val; | 710 | long long val; |
| 698 | int res; | 711 | int res; |
| 699 | 712 | ||
| 700 | while(*p == ' ') | 713 | while(*p == ' ') |
| 701 | ++p; | 714 | ++p; |
| 702 | 715 | ||
| 703 | val = strtoll(p, &q, 16); | 716 | val = strtoll(p, &q, 16); |
| 704 | if (q == p) | 717 | if (q == p) |
| 705 | error("event %llu field %d not an integer: %s", | 718 | error("event %llu field %d not an integer: %s", |
| 706 | count, index, p); | 719 | count, index, p); |
| 707 | 720 | ||
| 708 | res = sqlite3_bind_int64(stmt, index, val); | 721 | res = sqlite3_bind_int64(stmt, index, val); |
| 709 | if (res != SQLITE_OK) | 722 | if (res != SQLITE_OK) |
| 710 | sqlite_error(res, db, "event %llu field %d bind failed", count, index); | 723 | sqlite_error(res, db, "event %llu field %d bind failed", count, index); |
| 711 | return q; | 724 | return q; |
| 712 | } | 725 | } |
| 713 | 726 | ||
| 714 | static char *bind_real(sqlite3 *db, sqlite3_stmt *stmt, int64 count, int index, char *p) | 727 | static char *bind_real(sqlite3 *db, sqlite3_stmt *stmt, int64 count, int index, char *p) |
| 715 | { | 728 | { |
| 716 | char *q; | 729 | char *q; |
| 717 | double val; | 730 | double val; |
| 718 | int res; | 731 | int res; |
| 719 | 732 | ||
| 720 | while(*p == ' ') | 733 | while(*p == ' ') |
| 721 | ++p; | 734 | ++p; |
| 722 | 735 | ||
| 723 | val = strtod(p, &q); | 736 | val = strtod(p, &q); |
| 724 | if (q == p) | 737 | if (q == p) |
| 725 | error("event %llu field %d not a floating-point value: %s", | 738 | error("event %llu field %d not a floating-point value: %s", |
| 726 | count, index, p); | 739 | count, index, p); |
| 727 | 740 | ||
| 728 | res = sqlite3_bind_double(stmt, index, val); | 741 | res = sqlite3_bind_double(stmt, index, val); |
| 729 | if (res != SQLITE_OK) | 742 | if (res != SQLITE_OK) |
| 730 | sqlite_error(res, db, "event %llu field %d bind failed", count, index); | 743 | sqlite_error(res, db, "event %llu field %d bind failed", count, index); |
| 731 | return q; | 744 | return q; |
| 732 | } | 745 | } |
| 733 | 746 | ||
| 734 | static char *bind_text(sqlite3 *db, sqlite3_stmt *stmt, int64 count, int index, char *p) | 747 | static char *bind_text(sqlite3 *db, sqlite3_stmt *stmt, int64 count, int index, char *p) |
| 735 | { | 748 | { |
| 736 | char *q; | 749 | char *q; |
| 737 | int res; | 750 | int res; |
| 738 | 751 | ||
| 739 | while(*p == ' ') | 752 | while(*p == ' ') |
| 740 | ++p; | 753 | ++p; |
| 741 | 754 | ||
| 742 | q = p; | 755 | q = p; |
| 743 | while((*q != '\n') && (*q != '\0')) { | 756 | while((*q != '\n') && (*q != '\0')) { |
| 744 | ++ q; | 757 | ++ q; |
| 745 | } | 758 | } |
| 746 | if ((q == p) || (q[-1] != '"')) | 759 | if ((q == p) || (q[-1] != '"')) |
| 747 | error("event %llu string field %d has no closing quote mark.", | 760 | error("event %llu string field %d has no closing quote mark.", |
| 748 | count, index); | 761 | count, index); |
| 749 | 762 | ||
| 750 | res = sqlite3_bind_text(stmt, index, p, (int)(q-p-1), SQLITE_STATIC); | 763 | res = sqlite3_bind_text(stmt, index, p, (int)(q-p-1), SQLITE_STATIC); |
| 751 | if (res != SQLITE_OK) | 764 | if (res != SQLITE_OK) |
| 752 | sqlite_error(res, db, "event %llu field %d bind failed", count, index); | 765 | sqlite_error(res, db, "event %llu field %d bind failed", count, index); |
| 753 | return q; | 766 | return q; |
| 754 | } | 767 | } |
| 755 | 768 | ||
| 769 | /* this is overkill, at present. */ | ||
| 770 | |||
| 771 | #define MAX_LOG_LINE_LENGTH 1024 | ||
| 772 | |||
| 756 | /* readLog -- read and parse log. Returns the number of events written. | 773 | /* readLog -- read and parse log. Returns the number of events written. |
| 757 | */ | 774 | */ |
| 758 | 775 | ||
| 759 | static int64 readLog(FILE *input, | 776 | static int64 readLog(FILE *input, |
| 760 | sqlite3 *db) | 777 | sqlite3 *db) |
| 761 | { | 778 | { |
| 762 | int64 eventCount = 0; | 779 | int64 eventCount = 0; |
| 763 | 780 | ||
| 764 | /* declare statements for every event type */ | 781 | /* declare statements for every event type */ |
| 765 | EVENT_LIST(EVENT_TYPE_DECLARE_STATEMENT, X); | 782 | EVENT_LIST(EVENT_TYPE_DECLARE_STATEMENT, X); |
| 766 | 783 | ||
| 767 | /* prepare statements for every event type */ | 784 | /* prepare statements for every event type */ |
| 768 | EVENT_LIST(EVENT_TYPE_PREPARE_STATEMENT, X); | 785 | EVENT_LIST(EVENT_TYPE_PREPARE_STATEMENT, X); |
| 769 | 786 | ||
| 770 | runStatement(db, "BEGIN", "Transaction start"); | 787 | runStatement(db, "BEGIN", "Transaction start"); |
| 771 | 788 | ||
| 772 | while (TRUE) { /* loop for each event */ | 789 | while (TRUE) { /* loop for each event */ |
| 773 | char line[1024]; | 790 | char line[MAX_LOG_LINE_LENGTH]; |
| 774 | char *p; | 791 | char *p; |
| 775 | char *q; | 792 | char *q; |
| 776 | int last_index=0; | 793 | int last_index=0; |
| 777 | sqlite3_stmt *statement; | 794 | sqlite3_stmt *statement; |
| 778 | int res; | 795 | int res; |
| 779 | int64 clock; | 796 | int64 clock; |
| 780 | long code; | 797 | long code; |
| 781 | 798 | ||
| 782 | p = fgets(line, 1024, input); | 799 | p = fgets(line, MAX_LOG_LINE_LENGTH, input); |
| 783 | if (!p) { | 800 | if (!p) { |
| 784 | if (feof(input)) | 801 | if (feof(input)) |
| 785 | break; | 802 | break; |
| 786 | else | 803 | else |
| 787 | error("Couldn't read line after event %llu", eventCount); | 804 | error("Couldn't read line after event %llu", eventCount); |
| 788 | } | 805 | } |
| 789 | 806 | ||
| 790 | eventCount++; | 807 | eventCount++; |
| 791 | 808 | ||
| 792 | clock = strtoll(p, &q, 16); | 809 | clock = strtoll(p, &q, 16); |
| 793 | if (q == p) | 810 | |
| 794 | error("event %llu clock field not a hex integer: %s", | 811 | if (q == p) |
| 795 | eventCount, p); | 812 | error("event %llu clock field not a hex integer: %s", |
| 796 | 813 | eventCount, p); | |
| 797 | if (*q != ' ') | 814 | |
| 798 | error("event %llu code field not preceded by ' ': %s", | 815 | if (*q != ' ') |
| 799 | eventCount, q); | 816 | error("event %llu code field not preceded by ' ': %s", |
| 800 | while(*q == ' ') | 817 | eventCount, q); |
| 801 | ++q; | 818 | while(*q == ' ') |
| 802 | 819 | ++q; | |
| 803 | p = q; | 820 | |
| 804 | code = strtol(p, &q, 16); | 821 | p = q; |
| 805 | if (q == p) | 822 | code = strtol(p, &q, 16); |
| 806 | error("event %llu code field %d not an integer: %s", | 823 | if (q == p) |
| 807 | eventCount, index, p); | 824 | error("event %llu code field %d not an integer: %s", |
| 808 | p = q; | 825 | eventCount, index, p); |
| 809 | /* Write event to SQLite. */ | 826 | p = q; |
| 810 | switch (code) { | 827 | |
| 811 | /* this macro sets statement and last_index */ | 828 | /* Write event to SQLite. */ |
| 812 | EVENT_LIST(EVENT_TYPE_WRITE_SQL, X); | 829 | switch (code) { |
| 813 | default: | 830 | /* this macro sets statement and last_index */ |
| 814 | error("Event %llu has Unknown event code %d", eventCount, code); | 831 | EVENT_LIST(EVENT_TYPE_WRITE_SQL, X); |
| 815 | } | 832 | default: |
| 816 | /* bind the fields we store for every event */ \ | 833 | error("Event %llu has Unknown event code %d", eventCount, code); |
| 817 | res = sqlite3_bind_int64(statement, last_index+1, logSerial); | 834 | } |
| 818 | if (res != SQLITE_OK) | 835 | /* bind the fields we store for every event */ \ |
| 819 | sqlite_error(res, db, "Event %llu bind of log_serial failed.", eventCount); | 836 | res = sqlite3_bind_int64(statement, last_index+1, logSerial); |
| 820 | res = sqlite3_bind_int64(statement, last_index+2, clock); | 837 | if (res != SQLITE_OK) |
| 821 | if (res != SQLITE_OK) | 838 | sqlite_error(res, db, "Event %llu bind of log_serial failed.", eventCount); |
| 822 | sqlite_error(res, db, "Event %llu bind of clock failed.", eventCount); | 839 | res = sqlite3_bind_int64(statement, last_index+2, clock); |
| 823 | res = sqlite3_step(statement); | 840 | if (res != SQLITE_OK) |
| 824 | if (res != SQLITE_DONE) | 841 | sqlite_error(res, db, "Event %llu bind of clock failed.", eventCount); |
| 825 | sqlite_error(res, db, "insert of event %llu failed.", eventCount); | 842 | res = sqlite3_step(statement); |
| 826 | res = sqlite3_reset(statement); | 843 | if (res != SQLITE_DONE) |
| 827 | if (res != SQLITE_OK) | 844 | sqlite_error(res, db, "insert of event %llu failed.", eventCount); |
| 828 | sqlite_error(res, db, "Couldn't reset insert statement of event %llu", eventCount); | 845 | res = sqlite3_reset(statement); |
| 829 | 846 | if (res != SQLITE_OK) | |
| 830 | if (verbosity > LOG_ALWAYS) { | 847 | sqlite_error(res, db, "Couldn't reset insert statement of event %llu", eventCount); |
| 831 | if ((eventCount % SMALL_TICK) == 0) { | 848 | |
| 832 | printf("."); | 849 | if (progress) { |
| 833 | fflush(stdout); | 850 | if ((eventCount % SMALL_TICK) == 0) { |
| 834 | if (((eventCount / SMALL_TICK) % BIG_TICK) == 0) { | 851 | printf("."); |
| 835 | printf("\n"); | 852 | fflush(stdout); |
| 836 | fflush(stdout); | 853 | if (((eventCount / SMALL_TICK) % BIG_TICK) == 0) { |
| 837 | log(LOG_SOMETIMES, "%lu events.", (unsigned long)eventCount); | 854 | printf("\n"); |
| 838 | } | 855 | fflush(stdout); |
| 839 | } | 856 | log(LOG_SOMETIMES, "%lu events.", (unsigned long)eventCount); |
| 840 | } | ||
| 841 | } | ||
| 842 | if (verbosity > LOG_ALWAYS) { | ||
| 843 | printf("\n"); | ||
| 844 | fflush(stdout); | ||
| 845 | } | 857 | } |
| 846 | runStatement(db, "COMMIT", "Transaction finish"); | 858 | } |
| 847 | logFileCompleted(db, eventCount); | 859 | } |
| 860 | } | ||
| 861 | if (progress) { | ||
| 862 | printf("\n"); | ||
| 863 | fflush(stdout); | ||
| 864 | } | ||
| 865 | runStatement(db, "COMMIT", "Transaction finish"); | ||
| 866 | logFileCompleted(db, eventCount); | ||
| 848 | 867 | ||
| 849 | /* finalize all the statements */ | 868 | /* finalize all the statements */ |
| 850 | EVENT_LIST(EVENT_TYPE_FINALIZE_STATEMENT, X); | 869 | EVENT_LIST(EVENT_TYPE_FINALIZE_STATEMENT, X); |
| 851 | 870 | ||
| 852 | return eventCount; | 871 | return eventCount; |
| 853 | } | 872 | } |
| 854 | 873 | ||
| 874 | /* openLog -- open the log file doors, HAL */ | ||
| 875 | |||
| 855 | static FILE *openLog(sqlite3 *db) | 876 | static FILE *openLog(sqlite3 *db) |
| 856 | { | 877 | { |
| 857 | FILE *input; | 878 | FILE *input; |
| 858 | 879 | ||
| 859 | registerLogFile(db, logFileName); | 880 | registerLogFile(db, logFileName); |
| 860 | if (!logFileName) { | 881 | if (!logFileName) { |
| 861 | input = stdin; | 882 | input = stdin; |
| 862 | logFileName = "<stdin>"; | 883 | logFileName = "<stdin>"; |
| 863 | } else { | 884 | } else { |
| 864 | input = fopen(logFileName, "r"); | 885 | input = fopen(logFileName, "r"); |
| 865 | if (input == NULL) | 886 | if (input == NULL) |
| 866 | error("unable to open %s", logFileName); | 887 | error("unable to open %s", logFileName); |
| 867 | } | 888 | } |
| 868 | 889 | ||
| 869 | log(LOG_OFTEN, "Reading %s.", logFileName ? logFileName : "standard input"); | 890 | log(LOG_OFTEN, "Reading %s.", logFileName ? logFileName : "standard input"); |
| 870 | 891 | ||
| 871 | return input; | 892 | return input; |
| 872 | } | 893 | } |
| 873 | 894 | ||
| 874 | static int64 writeEventsToSQL(sqlite3 *db) | 895 | static int64 writeEventsToSQL(sqlite3 *db) |
| 875 | { | 896 | { |
| 876 | FILE *input; | 897 | FILE *input; |
| 877 | int64 count; | 898 | int64 count; |
| 878 | input = openLog(db); | 899 | input = openLog(db); |
| 879 | count = readLog(input, db); | 900 | count = readLog(input, db); |
| 880 | (void)fclose(input); | 901 | (void)fclose(input); |
| 881 | return count; | 902 | return count; |
| 882 | } | 903 | } |
| 883 | 904 | ||
| 884 | 905 | ||
| 885 | int main(int argc, char *argv[]) | 906 | int main(int argc, char *argv[]) |
| 886 | { | 907 | { |
| 887 | sqlite3 *db; | 908 | sqlite3 *db; |
| 888 | int64 count; | 909 | int64 count; |
| 889 | 910 | ||
| 890 | parseArgs(argc, argv); | 911 | parseArgs(argc, argv); |
| 891 | 912 | ||
| 892 | db = openDatabase(); | 913 | db = openDatabase(); |
| 893 | if (rebuild) { | 914 | if (rebuild) { |
| 894 | dropGlueTables(db); | 915 | dropGlueTables(db); |
| 895 | } | 916 | } |
| 896 | makeTables(db); | 917 | makeTables(db); |
| 897 | fillGlueTables(db); | 918 | fillGlueTables(db); |
| 898 | count = writeEventsToSQL(db); | 919 | count = writeEventsToSQL(db); |
| 899 | log(LOG_ALWAYS, "Imported %llu events from %s to %s, serial %lu.", | 920 | log(LOG_ALWAYS, "Imported %llu events from %s to %s, serial %lu.", |
| 900 | count, logFileName, databaseName, logSerial); | 921 | count, logFileName, databaseName, logSerial); |
| 901 | 922 | ||
| 902 | if (runTests) { | 923 | if (runTests) { |
| 903 | /* TODO: more unit tests in here */ | 924 | /* TODO: more unit tests in here */ |
| 904 | testTableExists(db); | 925 | testTableExists(db); |
| 905 | } | 926 | } |
| 906 | 927 | ||
| 907 | closeDatabase(db); | 928 | closeDatabase(db); |
| 908 | return 0; | 929 | return 0; |
| 909 | } | 930 | } |
| 910 | 931 | ||
| 911 | /* COPYRIGHT AND LICENSE | 932 | /* COPYRIGHT AND LICENSE |
diff --git a/mps/code/eventtxt.c b/mps/code/eventtxt.c new file mode 100644 index 00000000000..f923d52c66c --- /dev/null +++ b/mps/code/eventtxt.c | |||
| @@ -0,0 +1,534 @@ | |||
| 1 | /* eventtxt.c: event text log to human-friendly format. | ||
| 2 | * | ||
| 3 | * $Id$ | ||
| 4 | * | ||
| 5 | * Copyright (c) 2012 Ravenbrook Limited. See end of file for license. | ||
| 6 | * | ||
| 7 | * This is a command-line tool that converts events from a text-format | ||
| 8 | * MPS telemetry file into a more human-readable format. | ||
| 9 | * | ||
| 10 | * The default MPS library will write a binary-format telemetry file | ||
| 11 | * which can be converted into a text-format file using the eventcnv | ||
| 12 | * program (q.v.). | ||
| 13 | * | ||
| 14 | * For efficiency, eventcnv writes all event times, codes, and | ||
| 15 | * parameters (apart from EventFS - strings - and EventFD - | ||
| 16 | * floating-point) as hexadecimal strings, separated by single spaces. | ||
| 17 | * For human-readable purposes, we'd prefer a format in which | ||
| 18 | * parameters are named; event codes are converted to event type | ||
| 19 | * names; integers are in decimal; booleans are 'True ' or 'False'; | ||
| 20 | * pointers, addresses, and words are in hex; and labelled addresses | ||
| 21 | * are shown with their label strings. This program performs that | ||
| 22 | * conversion. | ||
| 23 | * | ||
| 24 | * Options: | ||
| 25 | * | ||
| 26 | * -l <logfile>: Import events from the named logfile. Defaults to | ||
| 27 | * stdin. | ||
| 28 | * | ||
| 29 | * $Id$ | ||
| 30 | */ | ||
| 31 | |||
| 32 | #include "config.h" | ||
| 33 | #include "eventdef.h" | ||
| 34 | #include "eventcom.h" | ||
| 35 | #include "table.h" | ||
| 36 | #include "testlib.h" /* for ulongest_t and associated print formats */ | ||
| 37 | |||
| 38 | #include <stdio.h> | ||
| 39 | #include <stdlib.h> | ||
| 40 | #include <string.h> | ||
| 41 | |||
| 42 | static char *prog; /* program name */ | ||
| 43 | static char *logFileName = NULL; | ||
| 44 | |||
| 45 | /* everror -- error signalling */ | ||
| 46 | |||
| 47 | static void everror(const char *format, ...) | ||
| 48 | { | ||
| 49 | va_list args; | ||
| 50 | |||
| 51 | fflush(stdout); /* sync */ | ||
| 52 | fprintf(stderr, "%s: ", prog); | ||
| 53 | va_start(args, format); | ||
| 54 | vfprintf(stderr, format, args); | ||
| 55 | fprintf(stderr, "\n"); | ||
| 56 | va_end(args); | ||
| 57 | exit(EXIT_FAILURE); | ||
| 58 | } | ||
| 59 | |||
| 60 | static void usage(void) | ||
| 61 | { | ||
| 62 | fprintf(stderr, | ||
| 63 | "Usage: %s [-l <logfile>]\n", | ||
| 64 | prog); | ||
| 65 | } | ||
| 66 | |||
| 67 | static void usageError(void) | ||
| 68 | { | ||
| 69 | usage(); | ||
| 70 | everror("Bad usage"); | ||
| 71 | } | ||
| 72 | |||
| 73 | /* parseArgs -- parse command line arguments */ | ||
| 74 | |||
| 75 | static void parseArgs(int argc, char *argv[]) | ||
| 76 | { | ||
| 77 | int i = 1; | ||
| 78 | |||
| 79 | if (argc >= 1) | ||
| 80 | prog = argv[0]; | ||
| 81 | else | ||
| 82 | prog = "unknown"; | ||
| 83 | |||
| 84 | while (i < argc) { /* consider argument i */ | ||
| 85 | if (argv[i][0] == '-') { /* it's an option argument */ | ||
| 86 | switch (argv[i][1]) { | ||
| 87 | case 'l': /* log file name */ | ||
| 88 | ++ i; | ||
| 89 | if (i == argc) | ||
| 90 | usageError(); | ||
| 91 | else | ||
| 92 | logFileName = argv[i]; | ||
| 93 | break; | ||
| 94 | case '?': case 'h': /* help */ | ||
| 95 | usage(); | ||
| 96 | break; | ||
| 97 | default: | ||
| 98 | usageError(); | ||
| 99 | } | ||
| 100 | } /* if option */ | ||
| 101 | ++ i; | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | /* table methods for a table of interned strings, and another of | ||
| 106 | * labelled addresses. */ | ||
| 107 | |||
| 108 | static void *tableAlloc(void *closure, size_t size) | ||
| 109 | { | ||
| 110 | UNUSED(closure); | ||
| 111 | return malloc(size); | ||
| 112 | } | ||
| 113 | |||
| 114 | static void tableFree(void *closure, void *p, size_t size) | ||
| 115 | { | ||
| 116 | UNUSED(closure); | ||
| 117 | UNUSED(size); | ||
| 118 | free(p); | ||
| 119 | } | ||
| 120 | |||
| 121 | /* Printing routines */ | ||
| 122 | |||
| 123 | /* printStr -- print an EventString */ | ||
| 124 | |||
| 125 | static void printStr(const char *str) | ||
| 126 | { | ||
| 127 | size_t i; | ||
| 128 | |||
| 129 | putchar('"'); | ||
| 130 | for (i = 0; str[i] != '\0'; ++i) { | ||
| 131 | char c = str[i]; | ||
| 132 | if (c == '"' || c == '\\') putchar('\\'); | ||
| 133 | putchar(c); | ||
| 134 | } | ||
| 135 | putchar('"'); | ||
| 136 | } | ||
| 137 | |||
| 138 | |||
| 139 | /* Reading hex numbers, and doubles, and quoted-and-escaped | ||
| 140 | * strings. */ | ||
| 141 | |||
| 142 | static ulongest_t parseHex(char **pInOut) | ||
| 143 | { | ||
| 144 | ulongest_t val; | ||
| 145 | int i, l; | ||
| 146 | char *p = *pInOut; | ||
| 147 | |||
| 148 | i = sscanf(p, "%" SCNXLONGEST "%n", &val, &l); | ||
| 149 | if (i != 1) | ||
| 150 | everror("Couldn't read a hex number from '%s'", p); | ||
| 151 | *pInOut = p + l; | ||
| 152 | return val; | ||
| 153 | } | ||
| 154 | |||
| 155 | static double parseDouble(char **pInOut) | ||
| 156 | { | ||
| 157 | double val; | ||
| 158 | int i, l; | ||
| 159 | char *p = *pInOut; | ||
| 160 | |||
| 161 | i = sscanf(p, "%lg%n", &val, &l); | ||
| 162 | if (i != 1) | ||
| 163 | everror("Couldn't read a float from '%s'", p); | ||
| 164 | *pInOut = p + l; | ||
| 165 | return val; | ||
| 166 | } | ||
| 167 | |||
| 168 | /* parseString checks string syntax (opening and closing quotation | ||
| 169 | * marks) and takes a copy (stripping escaping backslashes) into a | ||
| 170 | * static buffer (callers must "use it or lose it"; the next | ||
| 171 | * invocation will over-write it). Probably not bullet-proof. */ | ||
| 172 | |||
| 173 | #define MAX_STRING_LENGTH 1024 | ||
| 174 | |||
| 175 | char strBuf[MAX_STRING_LENGTH]; | ||
| 176 | |||
| 177 | static char *parseString(char **pInOut) | ||
| 178 | { | ||
| 179 | char *p = *pInOut; | ||
| 180 | char *q = strBuf; | ||
| 181 | while(*p == ' ') | ||
| 182 | ++p; | ||
| 183 | |||
| 184 | if (*p != '"') | ||
| 185 | everror("String has no opening quotation mark: '%s'", p); | ||
| 186 | ++p; | ||
| 187 | |||
| 188 | while(1) { | ||
| 189 | if (q - strBuf >= MAX_STRING_LENGTH) { | ||
| 190 | everror("String length exceeds %d", MAX_STRING_LENGTH); | ||
| 191 | } | ||
| 192 | if (*p == '\\') { /* escaped character */ | ||
| 193 | ++p; | ||
| 194 | if (*p == '\0') | ||
| 195 | everror("Closing NUL byte escaped by backslash."); | ||
| 196 | *q++ = *p++; | ||
| 197 | } else if (*p == '"') { /* end of string */ | ||
| 198 | *q = '\0'; | ||
| 199 | ++p; | ||
| 200 | *pInOut = p; | ||
| 201 | return strBuf; | ||
| 202 | } else if (*p == '\0') | ||
| 203 | everror("Unexpected closing NUL byte."); | ||
| 204 | else | ||
| 205 | *q++ = *p++; | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | /* Event logs have interned strings (i.e. they construct a partial | ||
| 210 | * function from non-negatie integer IDs to strings), and can label | ||
| 211 | * addresses with intern string IDs (i.e. they construct a partial | ||
| 212 | * function from address to string ID). We need two tables to keep | ||
| 213 | * track of these. */ | ||
| 214 | |||
| 215 | static Table internTable; /* dictionary of intern ids to strings */ | ||
| 216 | |||
| 217 | static Table labelTable; /* dictionary of addrs to intern ids */ | ||
| 218 | |||
| 219 | static void createTables(void) | ||
| 220 | { | ||
| 221 | Res res; | ||
| 222 | /* MPS intern IDs are serials from zero up, so we can use -1 | ||
| 223 | * and -2 as specials. */ | ||
| 224 | res = TableCreate(&internTable, | ||
| 225 | (size_t)1<<4, | ||
| 226 | tableAlloc, tableFree, NULL, | ||
| 227 | (Word)-1, (Word)-2); | ||
| 228 | if (res != ResOK) | ||
| 229 | everror("Couldn't make intern table."); | ||
| 230 | |||
| 231 | /* We assume that 0 and 1 are invalid as Addrs. */ | ||
| 232 | res = TableCreate(&labelTable, (size_t)1<<7, | ||
| 233 | tableAlloc, tableFree, NULL, | ||
| 234 | 0, 1); | ||
| 235 | if (res != ResOK) | ||
| 236 | everror("Couldn't make label table."); | ||
| 237 | } | ||
| 238 | |||
| 239 | /* intern -- record an interned string in the table. a copy of | ||
| 240 | * the string from the parsed buffer into a newly-allocated block. */ | ||
| 241 | |||
| 242 | static void intern(char *p) | ||
| 243 | { | ||
| 244 | ulongest_t stringId; | ||
| 245 | char *string; | ||
| 246 | char *copy; | ||
| 247 | size_t len; | ||
| 248 | Res res; | ||
| 249 | |||
| 250 | stringId = parseHex(&p); | ||
| 251 | string = parseString(&p); | ||
| 252 | len = strlen(string); | ||
| 253 | copy = malloc(len+1); | ||
| 254 | if (copy == NULL) | ||
| 255 | everror("Couldn't allocate space for a string."); | ||
| 256 | (void)strcpy(copy, string); | ||
| 257 | res = TableDefine(internTable, (Word)stringId, (void *)copy); | ||
| 258 | if (res != ResOK) | ||
| 259 | everror("Couldn't create an intern mapping."); | ||
| 260 | } | ||
| 261 | |||
| 262 | /* label records a label (an associations between addresses and string | ||
| 263 | * IDs). Note that the event log may have been generated on a | ||
| 264 | * platform with addresses larger than Word on the current platform. | ||
| 265 | * If that happens then we are scuppered because our Table code uses | ||
| 266 | * Word as the key type: there's nothing we can do except detect this | ||
| 267 | * bad case (see also the EventInit handling and warning code). | ||
| 268 | * | ||
| 269 | * We can and do handle the case where string IDs (which are Words on | ||
| 270 | * the MPS platform) are larger than void* on the current platform. | ||
| 271 | * This is probably in fact the same case, because Word should be the | ||
| 272 | * same size as void*. In practice, trying to analyse a log from a | ||
| 273 | * wide platform on a narrow one (e.g. - the only case which is likely | ||
| 274 | * to occur this decade - from a 64-bit platform on a 32-bit one) is | ||
| 275 | * probably a bad idea and maybe doomed to failure. | ||
| 276 | */ | ||
| 277 | |||
| 278 | static void label(char *p) | ||
| 279 | { | ||
| 280 | ulongest_t address; | ||
| 281 | ulongest_t *stringIdP; | ||
| 282 | Res res; | ||
| 283 | |||
| 284 | address = parseHex(&p); | ||
| 285 | if (address > (Word)-1) { | ||
| 286 | printf("label address too large!"); | ||
| 287 | return; | ||
| 288 | } | ||
| 289 | |||
| 290 | stringIdP = malloc(sizeof(ulongest_t)); | ||
| 291 | if (stringIdP == NULL) | ||
| 292 | everror("Can't allocate space for a string's ID"); | ||
| 293 | *stringIdP = parseHex(&p); | ||
| 294 | res = TableDefine(labelTable, (Word)address, (void *)stringIdP); | ||
| 295 | if (res != ResOK) | ||
| 296 | everror("Couldn't create an intern mapping."); | ||
| 297 | } | ||
| 298 | |||
| 299 | /* output code */ | ||
| 300 | |||
| 301 | /* hexWordWidth is the number of characters used to output a Word | ||
| 302 | * value in hexadecimal. Note that what we really care about is the | ||
| 303 | * width of a Word on the source platform, not here. So when we see | ||
| 304 | * an EventInit event, we update this variable to the necessary | ||
| 305 | * width. */ | ||
| 306 | |||
| 307 | static int hexWordWidth = (MPS_WORD_WIDTH+3)/4; | ||
| 308 | |||
| 309 | /* printAddr -- output a ulongest_t in hex, with the interned string | ||
| 310 | * if the value is in the label table */ | ||
| 311 | |||
| 312 | static void printAddr(ulongest_t addr, const char *ident) | ||
| 313 | { | ||
| 314 | ulongest_t label; | ||
| 315 | void *alias; | ||
| 316 | |||
| 317 | printf("%s:%0*" PRIXLONGEST, ident, hexWordWidth, addr); | ||
| 318 | if (TableLookup(&alias, labelTable, addr)) { | ||
| 319 | label = *(ulongest_t*)alias; | ||
| 320 | putchar('['); | ||
| 321 | if (TableLookup(&alias, internTable, label)) | ||
| 322 | printStr((char *)alias); | ||
| 323 | else | ||
| 324 | printf("unknown label %" PRIuLONGEST, label); | ||
| 325 | putchar(']'); | ||
| 326 | } | ||
| 327 | putchar(' '); | ||
| 328 | } | ||
| 329 | |||
| 330 | /* parameter processing. For each parameter we parse it and then | ||
| 331 | * print it, preceded by its name and a colon and followed by a | ||
| 332 | * space. */ | ||
| 333 | |||
| 334 | #define processParamA(ident) \ | ||
| 335 | val_hex = parseHex(&p); \ | ||
| 336 | printAddr(val_hex, #ident); | ||
| 337 | |||
| 338 | #define processParamP processParamA | ||
| 339 | #define processParamW processParamA | ||
| 340 | |||
| 341 | #define processParamU(ident) \ | ||
| 342 | val_hex = parseHex(&p); \ | ||
| 343 | printf(#ident ":%" PRIuLONGEST " ", val_hex); | ||
| 344 | |||
| 345 | #define processParamD(ident) \ | ||
| 346 | val_float = parseDouble(&p); \ | ||
| 347 | printf(#ident ":%#8.3g ", val_float); | ||
| 348 | |||
| 349 | #define processParamS(ident) \ | ||
| 350 | val_string = parseString(&p); \ | ||
| 351 | printf(#ident ":"); \ | ||
| 352 | printStr(val_string); \ | ||
| 353 | putchar(' '); | ||
| 354 | |||
| 355 | #define processParamB(ident) \ | ||
| 356 | val_hex = parseHex(&p); \ | ||
| 357 | printf(#ident ":%s ", val_hex ? "True" : "False"); | ||
| 358 | |||
| 359 | #define EVENT_PROCESS_PARAM(X, index, sort, ident) processParam##sort(ident); | ||
| 360 | |||
| 361 | #define EVENT_PROCESS(X, name, code, always, kind) \ | ||
| 362 | case code: \ | ||
| 363 | EVENT_##name##_PARAMS(EVENT_PROCESS_PARAM, X) \ | ||
| 364 | break; | ||
| 365 | |||
| 366 | /* a table of the event names */ | ||
| 367 | |||
| 368 | static const char *eventName[EventCodeMAX+EventCodeMAX]; | ||
| 369 | |||
| 370 | #define EVENT_SET_NAME(X, name, code, always, kind) \ | ||
| 371 | eventName[code] = #name; | ||
| 372 | |||
| 373 | /* this is overkill, at present. */ | ||
| 374 | |||
| 375 | #define MAX_LOG_LINE_LENGTH 1024 | ||
| 376 | |||
| 377 | /* readLog -- read and parse log. Returns the number of events written. */ | ||
| 378 | |||
| 379 | static void readLog(FILE *input) | ||
| 380 | { | ||
| 381 | int i; | ||
| 382 | |||
| 383 | for (i=0; i <= EventCodeMAX; ++i) | ||
| 384 | eventName[i] = NULL; | ||
| 385 | |||
| 386 | EVENT_LIST(EVENT_SET_NAME, X); | ||
| 387 | |||
| 388 | while (TRUE) { /* loop for each event */ | ||
| 389 | char line[MAX_LOG_LINE_LENGTH]; | ||
| 390 | char *p, *q; | ||
| 391 | ulongest_t clock; | ||
| 392 | int code; | ||
| 393 | ulongest_t val_hex; | ||
| 394 | double val_float; | ||
| 395 | const char *val_string; | ||
| 396 | |||
| 397 | p = fgets(line, MAX_LOG_LINE_LENGTH, input); | ||
| 398 | if (!p) { | ||
| 399 | if (feof(input)) | ||
| 400 | break; | ||
| 401 | else | ||
| 402 | everror("Couldn't read line from input."); | ||
| 403 | } | ||
| 404 | |||
| 405 | clock = parseHex(&p); | ||
| 406 | code = (int)parseHex(&p); | ||
| 407 | |||
| 408 | if (eventName[code]) | ||
| 409 | printf("%0*" PRIXLONGEST " %04x %-19s ", hexWordWidth, clock, code, | ||
| 410 | eventName[code]); | ||
| 411 | else | ||
| 412 | printf("%0*" PRIXLONGEST " %04x %-19s ", hexWordWidth, clock, code, | ||
| 413 | "[Unknown]"); | ||
| 414 | |||
| 415 | q = p; | ||
| 416 | |||
| 417 | /* for a few particular codes, we do local processing. */ | ||
| 418 | if (code == EventInternCode) { | ||
| 419 | intern(q); | ||
| 420 | } else if (code == EventLabelCode) { | ||
| 421 | label(q); | ||
| 422 | } else if (code == EventEventInitCode) { | ||
| 423 | ulongest_t major, median, minor, maxCode, maxNameLen, wordWidth, clocksPerSec; | ||
| 424 | major = parseHex(&q); /* EVENT_VERSION_MAJOR */ | ||
| 425 | median = parseHex(&q); /* EVENT_VERSION_MEDIAN */ | ||
| 426 | minor = parseHex(&q); /* EVENT_VERSION_MINOR */ | ||
| 427 | maxCode = parseHex(&q); /* EventCodeMAX */ | ||
| 428 | maxNameLen = parseHex(&q); /* EventNameMAX */ | ||
| 429 | wordWidth = parseHex(&q); /* MPS_WORD_WIDTH */ | ||
| 430 | clocksPerSec = parseHex(&q); /* mps_clocks_per_sec() */ | ||
| 431 | UNUSED(clocksPerSec); | ||
| 432 | UNUSED(maxNameLen); | ||
| 433 | |||
| 434 | if ((major != EVENT_VERSION_MAJOR) || | ||
| 435 | (median != EVENT_VERSION_MEDIAN) || | ||
| 436 | (minor != EVENT_VERSION_MINOR)) { | ||
| 437 | fprintf(stderr, "Event log version does not match: %d.%d.%d vs %d.%d.%d\n", | ||
| 438 | (int)major, (int)median, (int)minor, | ||
| 439 | EVENT_VERSION_MAJOR, | ||
| 440 | EVENT_VERSION_MEDIAN, | ||
| 441 | EVENT_VERSION_MINOR); | ||
| 442 | } | ||
| 443 | |||
| 444 | if (maxCode > EventCodeMAX) { | ||
| 445 | fprintf(stderr, "Event log may contain unknown events with codes from %d to %d\n", | ||
| 446 | EventCodeMAX+1, (int)maxCode); | ||
| 447 | } | ||
| 448 | |||
| 449 | if (wordWidth > MPS_WORD_WIDTH) { | ||
| 450 | int newHexWordWidth = (int)((wordWidth + 3) / 4); | ||
| 451 | if (newHexWordWidth > hexWordWidth) { | ||
| 452 | fprintf(stderr, | ||
| 453 | "Event log word width is greater than on current platform;" | ||
| 454 | "previous values may be printed too narrowly.\n"); | ||
| 455 | } | ||
| 456 | hexWordWidth = newHexWordWidth; | ||
| 457 | } | ||
| 458 | |||
| 459 | if (wordWidth > sizeof(ulongest_t) * CHAR_BIT) { | ||
| 460 | everror("Event log word width %d is too wide for the current platform.", | ||
| 461 | (int)wordWidth); | ||
| 462 | } | ||
| 463 | } | ||
| 464 | |||
| 465 | switch(code) { | ||
| 466 | EVENT_LIST(EVENT_PROCESS, X); | ||
| 467 | default: | ||
| 468 | printf("Unknown event."); | ||
| 469 | } | ||
| 470 | putchar('\n'); | ||
| 471 | |||
| 472 | } | ||
| 473 | } | ||
| 474 | |||
| 475 | int main(int argc, char *argv[]) | ||
| 476 | { | ||
| 477 | FILE *input; | ||
| 478 | |||
| 479 | parseArgs(argc, argv); | ||
| 480 | if (!logFileName) { | ||
| 481 | input = stdin; | ||
| 482 | logFileName = "<stdin>"; | ||
| 483 | } else { | ||
| 484 | input = fopen(logFileName, "r"); | ||
| 485 | if (input == NULL) | ||
| 486 | everror("unable to open %s", logFileName); | ||
| 487 | } | ||
| 488 | |||
| 489 | createTables(); | ||
| 490 | readLog(input); | ||
| 491 | (void)fclose(input); | ||
| 492 | return 0; | ||
| 493 | } | ||
| 494 | |||
| 495 | /* C. COPYRIGHT AND LICENSE | ||
| 496 | * | ||
| 497 | * Copyright (C) 2012 Ravenbrook Limited <http://www.ravenbrook.com/>. | ||
| 498 | * All rights reserved. This is an open source license. Contact | ||
| 499 | * Ravenbrook for commercial licensing options. | ||
| 500 | * | ||
| 501 | * Redistribution and use in source and binary forms, with or without | ||
| 502 | * modification, are permitted provided that the following conditions are | ||
| 503 | * met: | ||
| 504 | * | ||
| 505 | * 1. Redistributions of source code must retain the above copyright | ||
| 506 | * notice, this list of conditions and the following disclaimer. | ||
| 507 | * | ||
| 508 | * 2. Redistributions in binary form must reproduce the above copyright | ||
| 509 | * notice, this list of conditions and the following disclaimer in the | ||
| 510 | * documentation and/or other materials provided with the distribution. | ||
| 511 | * | ||
| 512 | * 3. Redistributions in any form must be accompanied by information on how | ||
| 513 | * to obtain complete source code for this software and any accompanying | ||
| 514 | * software that uses this software. The source code must either be | ||
| 515 | * included in the distribution or be available for no more than the cost | ||
| 516 | * of distribution plus a nominal fee, and must be freely redistributable | ||
| 517 | * under reasonable conditions. For an executable file, complete source | ||
| 518 | * code means the source code for all modules it contains. It does not | ||
| 519 | * include source code for modules or files that typically accompany the | ||
| 520 | * major components of the operating system on which the executable file | ||
| 521 | * runs. | ||
| 522 | * | ||
| 523 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS | ||
| 524 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | ||
| 525 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | ||
| 526 | * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE | ||
| 527 | * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
| 528 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
| 529 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
| 530 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
| 531 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 532 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
| 533 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 534 | */ | ||
diff --git a/mps/code/mps.xcodeproj/project.pbxproj b/mps/code/mps.xcodeproj/project.pbxproj index 0647bc91de2..9fe15daa36c 100644 --- a/mps/code/mps.xcodeproj/project.pbxproj +++ b/mps/code/mps.xcodeproj/project.pbxproj | |||
| @@ -54,6 +54,8 @@ | |||
| 54 | /* Begin PBXBuildFile section */ | 54 | /* Begin PBXBuildFile section */ |
| 55 | 2D07B97A1636FCCE00DB751B /* eventsql.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D07B96C1636FC7200DB751B /* eventsql.c */; }; | 55 | 2D07B97A1636FCCE00DB751B /* eventsql.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D07B96C1636FC7200DB751B /* eventsql.c */; }; |
| 56 | 2D07B97C163705E400DB751B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D07B97B163705E400DB751B /* libsqlite3.dylib */; }; | 56 | 2D07B97C163705E400DB751B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D07B97B163705E400DB751B /* libsqlite3.dylib */; }; |
| 57 | 2D53F2E716515A63009A1829 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; | ||
| 58 | 2D604BA516514C4F003AAF46 /* eventtxt.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D604BA416514C4F003AAF46 /* eventtxt.c */; }; | ||
| 57 | 3104AFBF156D3591000A585A /* apss.c in Sources */ = {isa = PBXBuildFile; fileRef = 3104AFBE156D3591000A585A /* apss.c */; }; | 59 | 3104AFBF156D3591000A585A /* apss.c in Sources */ = {isa = PBXBuildFile; fileRef = 3104AFBE156D3591000A585A /* apss.c */; }; |
| 58 | 3104AFC2156D35B2000A585A /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; | 60 | 3104AFC2156D35B2000A585A /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; |
| 59 | 3104AFC3156D35C3000A585A /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; | 61 | 3104AFC3156D35C3000A585A /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; |
| @@ -645,6 +647,15 @@ | |||
| 645 | ); | 647 | ); |
| 646 | runOnlyForDeploymentPostprocessing = 1; | 648 | runOnlyForDeploymentPostprocessing = 1; |
| 647 | }; | 649 | }; |
| 650 | 2D604B9A16514B1A003AAF46 /* CopyFiles */ = { | ||
| 651 | isa = PBXCopyFilesBuildPhase; | ||
| 652 | buildActionMask = 2147483647; | ||
| 653 | dstPath = /usr/share/man/man1/; | ||
| 654 | dstSubfolderSpec = 0; | ||
| 655 | files = ( | ||
| 656 | ); | ||
| 657 | runOnlyForDeploymentPostprocessing = 1; | ||
| 658 | }; | ||
| 648 | 3104AFB1156D357B000A585A /* CopyFiles */ = { | 659 | 3104AFB1156D357B000A585A /* CopyFiles */ = { |
| 649 | isa = PBXCopyFilesBuildPhase; | 660 | isa = PBXCopyFilesBuildPhase; |
| 650 | buildActionMask = 2147483647; | 661 | buildActionMask = 2147483647; |
| @@ -921,6 +932,8 @@ | |||
| 921 | 2D07B96C1636FC7200DB751B /* eventsql.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = eventsql.c; sourceTree = "<group>"; }; | 932 | 2D07B96C1636FC7200DB751B /* eventsql.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = eventsql.c; sourceTree = "<group>"; }; |
| 922 | 2D07B9711636FC9900DB751B /* eventsql */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = eventsql; sourceTree = BUILT_PRODUCTS_DIR; }; | 933 | 2D07B9711636FC9900DB751B /* eventsql */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = eventsql; sourceTree = BUILT_PRODUCTS_DIR; }; |
| 923 | 2D07B97B163705E400DB751B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; | 934 | 2D07B97B163705E400DB751B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; |
| 935 | 2D604B9C16514B1A003AAF46 /* eventtxt */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = eventtxt; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
| 936 | 2D604BA416514C4F003AAF46 /* eventtxt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = eventtxt.c; sourceTree = "<group>"; }; | ||
| 924 | 3104AFA5156D27E7000A585A /* ssixi6.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ssixi6.c; sourceTree = "<group>"; }; | 937 | 3104AFA5156D27E7000A585A /* ssixi6.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ssixi6.c; sourceTree = "<group>"; }; |
| 925 | 3104AFB3156D357B000A585A /* apss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = apss; sourceTree = BUILT_PRODUCTS_DIR; }; | 938 | 3104AFB3156D357B000A585A /* apss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = apss; sourceTree = BUILT_PRODUCTS_DIR; }; |
| 926 | 3104AFBE156D3591000A585A /* apss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = apss.c; sourceTree = "<group>"; }; | 939 | 3104AFBE156D3591000A585A /* apss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = apss.c; sourceTree = "<group>"; }; |
| @@ -1056,6 +1069,14 @@ | |||
| 1056 | ); | 1069 | ); |
| 1057 | runOnlyForDeploymentPostprocessing = 0; | 1070 | runOnlyForDeploymentPostprocessing = 0; |
| 1058 | }; | 1071 | }; |
| 1072 | 2D604B9916514B1A003AAF46 /* Frameworks */ = { | ||
| 1073 | isa = PBXFrameworksBuildPhase; | ||
| 1074 | buildActionMask = 2147483647; | ||
| 1075 | files = ( | ||
| 1076 | 2D53F2E716515A63009A1829 /* libmps.a in Frameworks */, | ||
| 1077 | ); | ||
| 1078 | runOnlyForDeploymentPostprocessing = 0; | ||
| 1079 | }; | ||
| 1059 | 3104AFB0156D357B000A585A /* Frameworks */ = { | 1080 | 3104AFB0156D357B000A585A /* Frameworks */ = { |
| 1060 | isa = PBXFrameworksBuildPhase; | 1081 | isa = PBXFrameworksBuildPhase; |
| 1061 | buildActionMask = 2147483647; | 1082 | buildActionMask = 2147483647; |
| @@ -1314,11 +1335,12 @@ | |||
| 1314 | name = eventsql; | 1335 | name = eventsql; |
| 1315 | sourceTree = "<group>"; | 1336 | sourceTree = "<group>"; |
| 1316 | }; | 1337 | }; |
| 1317 | 2D07B9731636FC9900DB751B /* eventsql */ = { | 1338 | 2D604B971651433C003AAF46 /* eventtxt */ = { |
| 1318 | isa = PBXGroup; | 1339 | isa = PBXGroup; |
| 1319 | children = ( | 1340 | children = ( |
| 1341 | 2D604BA416514C4F003AAF46 /* eventtxt.c */, | ||
| 1320 | ); | 1342 | ); |
| 1321 | path = eventsql; | 1343 | name = eventtxt; |
| 1322 | sourceTree = "<group>"; | 1344 | sourceTree = "<group>"; |
| 1323 | }; | 1345 | }; |
| 1324 | 3114A647156E956C001E0AA3 /* Mysterious */ = { | 1346 | 3114A647156E956C001E0AA3 /* Mysterious */ = { |
| @@ -1333,6 +1355,7 @@ | |||
| 1333 | 3114A6D6156E9846001E0AA3 /* Tools */ = { | 1355 | 3114A6D6156E9846001E0AA3 /* Tools */ = { |
| 1334 | isa = PBXGroup; | 1356 | isa = PBXGroup; |
| 1335 | children = ( | 1357 | children = ( |
| 1358 | 2D604B971651433C003AAF46 /* eventtxt */, | ||
| 1336 | 2D07B96A1636FC4C00DB751B /* eventsql */, | 1359 | 2D07B96A1636FC4C00DB751B /* eventsql */, |
| 1337 | 3114A6D8156E9942001E0AA3 /* eventcnv */, | 1360 | 3114A6D8156E9942001E0AA3 /* eventcnv */, |
| 1338 | ); | 1361 | ); |
| @@ -1425,7 +1448,6 @@ | |||
| 1425 | 3114A647156E956C001E0AA3 /* Mysterious */, | 1448 | 3114A647156E956C001E0AA3 /* Mysterious */, |
| 1426 | 31A47BA8156C1E930039B1C2 /* MPS */, | 1449 | 31A47BA8156C1E930039B1C2 /* MPS */, |
| 1427 | 3124CAB3156BE1B700753214 /* Tests */, | 1450 | 3124CAB3156BE1B700753214 /* Tests */, |
| 1428 | 2D07B9731636FC9900DB751B /* eventsql */, | ||
| 1429 | 31EEABEF156AAF5C00714D05 /* Products */, | 1451 | 31EEABEF156AAF5C00714D05 /* Products */, |
| 1430 | ); | 1452 | ); |
| 1431 | sourceTree = "<group>"; | 1453 | sourceTree = "<group>"; |
| @@ -1465,6 +1487,7 @@ | |||
| 1465 | 3114A6AC156E9759001E0AA3 /* walkt0 */, | 1487 | 3114A6AC156E9759001E0AA3 /* walkt0 */, |
| 1466 | 3114A6C6156E9815001E0AA3 /* eventcnv */, | 1488 | 3114A6C6156E9815001E0AA3 /* eventcnv */, |
| 1467 | 2D07B9711636FC9900DB751B /* eventsql */, | 1489 | 2D07B9711636FC9900DB751B /* eventsql */, |
| 1490 | 2D604B9C16514B1A003AAF46 /* eventtxt */, | ||
| 1468 | ); | 1491 | ); |
| 1469 | name = Products; | 1492 | name = Products; |
| 1470 | sourceTree = "<group>"; | 1493 | sourceTree = "<group>"; |
| @@ -1581,6 +1604,23 @@ | |||
| 1581 | productReference = 2D07B9711636FC9900DB751B /* eventsql */; | 1604 | productReference = 2D07B9711636FC9900DB751B /* eventsql */; |
| 1582 | productType = "com.apple.product-type.tool"; | 1605 | productType = "com.apple.product-type.tool"; |
| 1583 | }; | 1606 | }; |
| 1607 | 2D604B9B16514B1A003AAF46 /* eventtxt */ = { | ||
| 1608 | isa = PBXNativeTarget; | ||
| 1609 | buildConfigurationList = 2D604BA216514B59003AAF46 /* Build configuration list for PBXNativeTarget "eventtxt" */; | ||
| 1610 | buildPhases = ( | ||
| 1611 | 2D604B9816514B1A003AAF46 /* Sources */, | ||
| 1612 | 2D604B9916514B1A003AAF46 /* Frameworks */, | ||
| 1613 | 2D604B9A16514B1A003AAF46 /* CopyFiles */, | ||
| 1614 | ); | ||
| 1615 | buildRules = ( | ||
| 1616 | ); | ||
| 1617 | dependencies = ( | ||
| 1618 | ); | ||
| 1619 | name = eventtxt; | ||
| 1620 | productName = eventtxt; | ||
| 1621 | productReference = 2D604B9C16514B1A003AAF46 /* eventtxt */; | ||
| 1622 | productType = "com.apple.product-type.tool"; | ||
| 1623 | }; | ||
| 1584 | 3104AFB2156D357B000A585A /* apss */ = { | 1624 | 3104AFB2156D357B000A585A /* apss */ = { |
| 1585 | isa = PBXNativeTarget; | 1625 | isa = PBXNativeTarget; |
| 1586 | buildConfigurationList = 3104AFBC156D357B000A585A /* Build configuration list for PBXNativeTarget "apss" */; | 1626 | buildConfigurationList = 3104AFBC156D357B000A585A /* Build configuration list for PBXNativeTarget "apss" */; |
| @@ -2191,6 +2231,7 @@ | |||
| 2191 | 3114A6AB156E9759001E0AA3 /* walkt0 */, | 2231 | 3114A6AB156E9759001E0AA3 /* walkt0 */, |
| 2192 | 3114A6C5156E9815001E0AA3 /* eventcnv */, | 2232 | 3114A6C5156E9815001E0AA3 /* eventcnv */, |
| 2193 | 2D07B9701636FC9900DB751B /* eventsql */, | 2233 | 2D07B9701636FC9900DB751B /* eventsql */, |
| 2234 | 2D604B9B16514B1A003AAF46 /* eventtxt */, | ||
| 2194 | ); | 2235 | ); |
| 2195 | }; | 2236 | }; |
| 2196 | /* End PBXProject section */ | 2237 | /* End PBXProject section */ |
| @@ -2204,6 +2245,14 @@ | |||
| 2204 | ); | 2245 | ); |
| 2205 | runOnlyForDeploymentPostprocessing = 0; | 2246 | runOnlyForDeploymentPostprocessing = 0; |
| 2206 | }; | 2247 | }; |
| 2248 | 2D604B9816514B1A003AAF46 /* Sources */ = { | ||
| 2249 | isa = PBXSourcesBuildPhase; | ||
| 2250 | buildActionMask = 2147483647; | ||
| 2251 | files = ( | ||
| 2252 | 2D604BA516514C4F003AAF46 /* eventtxt.c in Sources */, | ||
| 2253 | ); | ||
| 2254 | runOnlyForDeploymentPostprocessing = 0; | ||
| 2255 | }; | ||
| 2207 | 3104AFAF156D357B000A585A /* Sources */ = { | 2256 | 3104AFAF156D357B000A585A /* Sources */ = { |
| 2208 | isa = PBXSourcesBuildPhase; | 2257 | isa = PBXSourcesBuildPhase; |
| 2209 | buildActionMask = 2147483647; | 2258 | buildActionMask = 2147483647; |
| @@ -2873,6 +2922,27 @@ | |||
| 2873 | }; | 2922 | }; |
| 2874 | name = WE; | 2923 | name = WE; |
| 2875 | }; | 2924 | }; |
| 2925 | 2D604B9F16514B1A003AAF46 /* Debug */ = { | ||
| 2926 | isa = XCBuildConfiguration; | ||
| 2927 | buildSettings = { | ||
| 2928 | PRODUCT_NAME = "$(TARGET_NAME)"; | ||
| 2929 | }; | ||
| 2930 | name = Debug; | ||
| 2931 | }; | ||
| 2932 | 2D604BA016514B1A003AAF46 /* Release */ = { | ||
| 2933 | isa = XCBuildConfiguration; | ||
| 2934 | buildSettings = { | ||
| 2935 | PRODUCT_NAME = "$(TARGET_NAME)"; | ||
| 2936 | }; | ||
| 2937 | name = Release; | ||
| 2938 | }; | ||
| 2939 | 2D604BA116514B1A003AAF46 /* WE */ = { | ||
| 2940 | isa = XCBuildConfiguration; | ||
| 2941 | buildSettings = { | ||
| 2942 | PRODUCT_NAME = "$(TARGET_NAME)"; | ||
| 2943 | }; | ||
| 2944 | name = WE; | ||
| 2945 | }; | ||
| 2876 | 3104AFBA156D357B000A585A /* Debug */ = { | 2946 | 3104AFBA156D357B000A585A /* Debug */ = { |
| 2877 | isa = XCBuildConfiguration; | 2947 | isa = XCBuildConfiguration; |
| 2878 | buildSettings = { | 2948 | buildSettings = { |
| @@ -3728,6 +3798,17 @@ | |||
| 3728 | 2D07B9771636FC9900DB751B /* WE */, | 3798 | 2D07B9771636FC9900DB751B /* WE */, |
| 3729 | ); | 3799 | ); |
| 3730 | defaultConfigurationIsVisible = 0; | 3800 | defaultConfigurationIsVisible = 0; |
| 3801 | defaultConfigurationName = Release; | ||
| 3802 | }; | ||
| 3803 | 2D604BA216514B59003AAF46 /* Build configuration list for PBXNativeTarget "eventtxt" */ = { | ||
| 3804 | isa = XCConfigurationList; | ||
| 3805 | buildConfigurations = ( | ||
| 3806 | 2D604B9F16514B1A003AAF46 /* Debug */, | ||
| 3807 | 2D604BA016514B1A003AAF46 /* Release */, | ||
| 3808 | 2D604BA116514B1A003AAF46 /* WE */, | ||
| 3809 | ); | ||
| 3810 | defaultConfigurationIsVisible = 0; | ||
| 3811 | defaultConfigurationName = Release; | ||
| 3731 | }; | 3812 | }; |
| 3732 | 3104AFBC156D357B000A585A /* Build configuration list for PBXNativeTarget "apss" */ = { | 3813 | 3104AFBC156D357B000A585A /* Build configuration list for PBXNativeTarget "apss" */ = { |
| 3733 | isa = XCConfigurationList; | 3814 | isa = XCConfigurationList; |
diff --git a/mps/code/testlib.h b/mps/code/testlib.h index 2bcd50e7c18..e3a11c34139 100644 --- a/mps/code/testlib.h +++ b/mps/code/testlib.h | |||
| @@ -74,31 +74,41 @@ | |||
| 74 | 74 | ||
| 75 | /* ulongest_t -- longest unsigned integer type | 75 | /* ulongest_t -- longest unsigned integer type |
| 76 | * | 76 | * |
| 77 | * Define a longest unsigned integer type for testing and printing. We'd | 77 | * Define a longest unsigned integer type for testing, scanning, and |
| 78 | * like to use C99's uintmax_t and PRIuMAX here, but the MPS is in C89 | 78 | * printing. We'd like to use C99's uintmax_t and PRIuMAX here, but |
| 79 | * and C99 isn't supported by Microsoft. | 79 | * the MPS is in C89 and C99 isn't supported by Microsoft. |
| 80 | * | 80 | * |
| 81 | * We avoid using the ones defined in mpstd.h because we want the tests to | 81 | * We avoid using the types defined in mpstd.h because we want the |
| 82 | * root out any incompatible assumptions by breaking. | 82 | * tests to root out any incompatible assumptions by breaking. |
| 83 | */ | 83 | */ |
| 84 | 84 | ||
| 85 | #if defined(MPS_ARCH_I6) | ||
| 86 | #define PRIwWORD "16" | ||
| 87 | #elif defined(MPS_ARCH_I3) | ||
| 88 | #define PRIwWORD "8" | ||
| 89 | #else | ||
| 90 | #error "How many beans make five?" | ||
| 91 | #endif | ||
| 92 | |||
| 85 | #ifdef MPS_PF_W3I6MV | 93 | #ifdef MPS_PF_W3I6MV |
| 86 | #define PRIuLONGEST "llu" | 94 | #define PRIuLONGEST "llu" |
| 87 | #define SCNuLONGEST "llu" | 95 | #define SCNuLONGEST "llu" |
| 96 | #define SCNXLONGEST "llX" | ||
| 88 | #define PRIXLONGEST "llX" | 97 | #define PRIXLONGEST "llX" |
| 89 | #define PRIwWORD "16" | ||
| 90 | typedef unsigned long long ulongest_t; | 98 | typedef unsigned long long ulongest_t; |
| 91 | typedef long long longest_t; | 99 | typedef long long longest_t; |
| 92 | #define MPS_WORD_CONST(n) (n##ull) | 100 | #define MPS_WORD_CONST(n) (n##ull) |
| 93 | #else | 101 | #else |
| 94 | #define PRIuLONGEST "lu" | 102 | #define PRIuLONGEST "lu" |
| 95 | #define SCNuLONGEST "lu" | 103 | #define SCNuLONGEST "lu" |
| 104 | #define SCNXLONGEST "lX" | ||
| 96 | #define PRIXLONGEST "lX" | 105 | #define PRIXLONGEST "lX" |
| 97 | #define PRIwWORD "8" | ||
| 98 | typedef unsigned long ulongest_t; | 106 | typedef unsigned long ulongest_t; |
| 99 | typedef long longest_t; | 107 | typedef long longest_t; |
| 100 | #define MPS_WORD_CONST(n) (n##ul) | 108 | #define MPS_WORD_CONST(n) (n##ul) |
| 101 | #endif | 109 | #endif |
| 110 | |||
| 111 | |||
| 102 | #define PRIXPTR "0"PRIwWORD PRIXLONGEST | 112 | #define PRIXPTR "0"PRIwWORD PRIXLONGEST |
| 103 | 113 | ||
| 104 | 114 | ||