aboutsummaryrefslogtreecommitdiffstats
path: root/mps/code
diff options
context:
space:
mode:
authorNick Barnes2012-11-13 12:51:58 +0000
committerNick Barnes2012-11-13 12:51:58 +0000
commit686ee3810172f6c8a0a25042c64698adc048506d (patch)
tree02621af982939a87073d7b1bd8a4f06d54699887 /mps/code
parent1330a1a99cdc425d308cfa0f0f50d161bfe9f1e2 (diff)
downloademacs-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.c62
-rw-r--r--mps/code/eventsql.c1029
-rw-r--r--mps/code/eventtxt.c534
-rw-r--r--mps/code/mps.xcodeproj/project.pbxproj87
-rw-r--r--mps/code/testlib.h24
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
144static void printHex(ulongest_t val) 157static 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
155static void printParamD(double d) 168static void printParamD(double d)
156{ 169{
157 printf(" %.10G", d); 170 printf(" %.10G", d);
158} 171}
159 172
160static void printParamS(const char *str) 173static 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
91typedef sqlite3_int64 int64; 83typedef 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
111static void vlog(unsigned int level, const char *format, va_list args) 103static 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
121static void log(unsigned int level, const char *format, ...) 113static 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
129static void error(const char *format, ...) 121static 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
139static void sqlite_error(int res, sqlite3 *db, const char *format, ...) 131static 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
150static char *prog; /* program name */ 144static char *prog; /* program name */
151static int rebuild = FALSE; 145static int rebuild = FALSE;
146static int deleteDatabase = FALSE;
152static int runTests = FALSE; 147static int runTests = FALSE;
153static int force = FALSE; 148static int force = FALSE;
149static int progress = FALSE;
154static char *databaseName = NULL; 150static char *databaseName = NULL;
155static char *logFileName = NULL; 151static char *logFileName = NULL;
156 152
157static void usage(void) 153static 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
164static void usageError(void) 171static 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
172static void parseArgs(int argc, char *argv[]) 179static 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
224static sqlite3 *openDatabase(void) 228static 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
249static void closeDatabase(sqlite3 *db) 262static 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)
259static sqlite3_stmt *prepareStatement(sqlite3 *db, 272static 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
274static void finalizeStatement(sqlite3 *db, 287static 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
283static void runStatement(sqlite3 *db, 296static 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
301static int tableExists(sqlite3* db, const char *tableName) 314static 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
329static struct { 342static 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
338static void testTableExists(sqlite3 *db) 351static 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
371static int64 logSerial = 0; 384static int64 logSerial = 0;
@@ -373,97 +386,97 @@ static int64 logSerial = 0;
373static void registerLogFile(sqlite3 *db, 386static 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
448static void logFileCompleted(sqlite3 *db, 461static 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
490const char *createStatements[] = { 503const 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
513EVENT_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
518static void makeTables(sqlite3 *db) 531static 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
528const char *glueTables[] = { 541const char *glueTables[] = {
529 "event_kind", 542 "event_kind",
530 "event_type", 543 "event_type",
531 "event_param", 544 "event_param",
532}; 545};
533 546
534static void dropGlueTables(sqlite3 *db) 547static 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
624static void fillGlueTables(sqlite3 *db) 637static 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
694static char *bind_int(sqlite3 *db, sqlite3_stmt *stmt, int64 count, int index, char *p) 707static 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
714static char *bind_real(sqlite3 *db, sqlite3_stmt *stmt, int64 count, int index, char *p) 727static 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
734static char *bind_text(sqlite3 *db, sqlite3_stmt *stmt, int64 count, int index, char *p) 747static 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
759static int64 readLog(FILE *input, 776static 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
855static FILE *openLog(sqlite3 *db) 876static 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
874static int64 writeEventsToSQL(sqlite3 *db) 895static 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
885int main(int argc, char *argv[]) 906int 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
42static char *prog; /* program name */
43static char *logFileName = NULL;
44
45/* everror -- error signalling */
46
47static 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
60static void usage(void)
61{
62 fprintf(stderr,
63 "Usage: %s [-l <logfile>]\n",
64 prog);
65}
66
67static void usageError(void)
68{
69 usage();
70 everror("Bad usage");
71}
72
73/* parseArgs -- parse command line arguments */
74
75static 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
108static void *tableAlloc(void *closure, size_t size)
109{
110 UNUSED(closure);
111 return malloc(size);
112}
113
114static 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
125static 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
142static 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
155static 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
175char strBuf[MAX_STRING_LENGTH];
176
177static 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
215static Table internTable; /* dictionary of intern ids to strings */
216
217static Table labelTable; /* dictionary of addrs to intern ids */
218
219static 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
242static 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
278static 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
307static 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
312static 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
368static 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
379static 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
475int 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"
90typedef unsigned long long ulongest_t; 98typedef unsigned long long ulongest_t;
91typedef long long longest_t; 99typedef 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"
98typedef unsigned long ulongest_t; 106typedef unsigned long ulongest_t;
99typedef long longest_t; 107typedef 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