aboutsummaryrefslogtreecommitdiffstats
path: root/mps/code
diff options
context:
space:
mode:
authorNick Barnes2012-10-15 00:32:37 +0100
committerNick Barnes2012-10-15 00:32:37 +0100
commitfc5acb36a7ea08dd12a173094d1679e5335a5cec (patch)
treeb3b7c5c60d44d0f6d70ca22f7dfa5444e344919e /mps/code
parent54a4a5568f783e01cafabe1aa110697aaf9b5c9a (diff)
downloademacs-fc5acb36a7ea08dd12a173094d1679e5335a5cec.tar.gz
emacs-fc5acb36a7ea08dd12a173094d1679e5335a5cec.zip
Functioning event/sql interface.
Copied from Perforce Change: 179880 ServerID: perforce.ravenbrook.com
Diffstat (limited to 'mps/code')
-rw-r--r--mps/code/eventsql.c405
1 files changed, 346 insertions, 59 deletions
diff --git a/mps/code/eventsql.c b/mps/code/eventsql.c
index 23a50f8063f..f1905231e3a 100644
--- a/mps/code/eventsql.c
+++ b/mps/code/eventsql.c
@@ -5,9 +5,26 @@
5 5
6#include <stdio.h> 6#include <stdio.h>
7#include <stdlib.h> 7#include <stdlib.h>
8#include <string.h>
8#include <sqlite3.h> 9#include <sqlite3.h>
9 10
10unsigned int verbosity = 4; 11#define DATABASE_NAME_ENVAR "MPS_EVENT_DATABASE"
12#define DEFAULT_DATABASE_NAME "mpsevent.db"
13
14#define TELEMETRY_FILENAME_ENVAR "MPS_TELEMETRY_FILENAME"
15#define DEFAULT_TELEMETRY_FILENAME "mpsio.log"
16
17/* we output rows of dots. One dot per SMALL_TICK events,
18 * BIG_TICK dots per row. */
19
20#define SMALL_TICK 1000
21#define BIG_TICK 50
22
23/* Utility code for logging to stderr with multiple log levels,
24 * and for reporting errors.
25 */
26
27unsigned int verbosity = 4; /* TODO command-line -v switches */
11 28
12#define LOG_ALWAYS 0 29#define LOG_ALWAYS 0
13#define LOG_OFTEN 1 30#define LOG_OFTEN 1
@@ -33,8 +50,6 @@ static void log(unsigned int level, const char *format, ...)
33 va_end(args); 50 va_end(args);
34} 51}
35 52
36#if 0 /* UNUSED */
37
38static void error(const char *format, ...) 53static void error(const char *format, ...)
39{ 54{
40 va_list args; 55 va_list args;
@@ -44,7 +59,6 @@ static void error(const char *format, ...)
44 va_end(args); 59 va_end(args);
45 exit(1); 60 exit(1);
46} 61}
47#endif /* UNUSED */
48 62
49static void sqlite_error(int res, sqlite3 *db, const char *format, ...) 63static void sqlite_error(int res, sqlite3 *db, const char *format, ...)
50{ 64{
@@ -57,15 +71,18 @@ static void sqlite_error(int res, sqlite3 *db, const char *format, ...)
57 exit(1); 71 exit(1);
58} 72}
59 73
60static void openDatabase(sqlite3 **dbReturn) 74/* openDatabase(p) opens the database file and returns a SQLite 3
75 * database connection object. */
76
77static sqlite3 *openDatabase(void)
61{ 78{
62 sqlite3 *db; 79 sqlite3 *db;
63 int res; 80 int res;
64 81
65 const char *filename = getenv("MPS_EVENT_DATABASE"); 82 const char *filename = getenv(DATABASE_NAME_ENVAR);
66 if(filename == NULL) 83 if(filename == NULL)
67 filename = "mpsevent.db"; 84 filename = DEFAULT_DATABASE_NAME;
68 log(LOG_OFTEN, "Opening %s.", filename); 85 log(LOG_ALWAYS, "Opening %s.", filename);
69 86
70 res = sqlite3_open_v2(filename, 87 res = sqlite3_open_v2(filename,
71 &db, 88 &db,
@@ -74,8 +91,99 @@ static void openDatabase(sqlite3 **dbReturn)
74 91
75 if (res != SQLITE_OK) 92 if (res != SQLITE_OK)
76 sqlite_error(res, db, "Opening %s failed", filename); 93 sqlite_error(res, db, "Opening %s failed", filename);
77 *dbReturn = db; 94 return db;
78 return; 95}
96
97/* closeDatabase(db) closes the database opened by openDatabase(). */
98
99static void closeDatabase(sqlite3 *db)
100{
101 int res = sqlite3_close(db);
102 if (res != SQLITE_OK)
103 sqlite_error(res, db, "Closing database failed");
104 log(LOG_ALWAYS, "Closed database.");
105}
106
107/* We need to be able to test for the existence of a table. The
108 * SQLite3 API seems to have no way to explore metadata like this,
109 * unless it is compiled in a particular way (in which case the
110 * function sqlite3_table_column_metadata could be used). Without
111 * that assistance, we can use a simple SQL trick (which could also
112 * tell us the number of rows in the table if we cared): */
113
114static int tableExists(sqlite3* db, const char *tableName)
115{
116 const char *format = "SELECT SUM(1) FROM %s";
117 char *sql;
118 int res;
119
120 sql = malloc(strlen(format) + strlen(tableName));
121 if (!sql)
122 error("Out of memory.");
123 sprintf(sql, format, tableName);
124 res = sqlite3_exec(db,
125 sql,
126 NULL, /* put in a callback here if we really want to know the number of rows */
127 NULL, /* callback closure */
128 NULL); /* error messages handled by sqlite_error */
129 switch(res) {
130 case SQLITE_OK:
131 return 1; /* table exists */
132 break;
133 case SQLITE_ERROR:
134 return 0; /* table does not exist; we can
135 probably do a better test for this case. */
136 break;
137 default:
138 sqlite_error(res, db, "Table test failed: %s", tableName);
139 }
140 /* UNREACHED */
141 return 0;
142}
143
144/* Unit test for tableExists() */
145
146static const char *tableTests[] = {
147 "event_kind",
148 "spong",
149 "EVENT_SegSplit",
150};
151
152static void testTableExists(sqlite3 *db)
153{
154 int i;
155 for (i=0; i < (sizeof(tableTests)/sizeof(tableTests[0])); ++i) {
156 if (tableExists(db, tableTests[i]))
157 printf("Table exists: %s\n", tableTests[i]);
158 else
159 printf("Table does not exist: %s\n", tableTests[i]);
160 }
161}
162
163/* Utility functions for SQLite statements. */
164
165static sqlite3_stmt *prepareStatement(sqlite3 *db,
166 const char *sql)
167{
168 int res;
169 sqlite3_stmt *statement;
170 log(LOG_SELDOM, "Preparing statement %s", sql);
171 res = sqlite3_prepare_v2(db, sql,
172 -1, /* prepare whole string as statement */
173 &statement,
174 NULL);
175 if (res != SQLITE_OK)
176 sqlite_error(res, db, "statementpreparation failed: %s", sql);
177 return statement;
178}
179
180static void finalizeStatement(sqlite3 *db,
181 sqlite3_stmt *statement)
182{
183 int res;
184 res = sqlite3_finalize(statement);
185 if (res != SQLITE_OK)
186 sqlite_error(res, db, "event_type finalize failed");
79} 187}
80 188
81/* Macro magic to make a CREATE TABLE statement for each event type. */ 189/* Macro magic to make a CREATE TABLE statement for each event type. */
@@ -94,7 +202,10 @@ static void openDatabase(sqlite3 **dbReturn)
94#define EVENT_TABLE_CREATE(X, name, code, always, kind) \ 202#define EVENT_TABLE_CREATE(X, name, code, always, kind) \
95 "CREATE TABLE IF NOT EXISTS EVENT_" #name " ( " \ 203 "CREATE TABLE IF NOT EXISTS EVENT_" #name " ( " \
96 EVENT_##name##_PARAMS(EVENT_PARAM_SQL_COLUMN, X) \ 204 EVENT_##name##_PARAMS(EVENT_PARAM_SQL_COLUMN, X) \
97 "time INTEGER ) ", 205 "time INTEGER, " \
206 "log_serial INTEGER)",
207
208/* An array of table-creation statement strings. */
98 209
99const char *createStatements[] = { 210const char *createStatements[] = {
100 "CREATE TABLE IF NOT EXISTS event_kind (name TEXT," 211 "CREATE TABLE IF NOT EXISTS event_kind (name TEXT,"
@@ -107,11 +218,37 @@ const char *createStatements[] = {
107 " kind INTEGER," 218 " kind INTEGER,"
108 " FOREIGN KEY (kind) REFERENCES event_kind(enum));", 219 " FOREIGN KEY (kind) REFERENCES event_kind(enum));",
109 220
221 "CREATE TABLE IF NOT EXISTS event_log (name TEXT,"
222 " file_id INTEGER,"
223 " size INTEGER,"
224 " time_sec INTEGER,"
225 " time_nsec INTEGER,"
226 " completed INTEGER,"
227 " serial INTEGER PRIMARY KEY AUTOINCREMENT)",
228
110EVENT_LIST(EVENT_TABLE_CREATE, X) 229EVENT_LIST(EVENT_TABLE_CREATE, X)
111}; 230};
112 231
113const char *populateStatements[] = { 232/* makeTables makes all the tables. */
114}; 233
234static void makeTables(sqlite3 *db)
235{
236 int i;
237 int res;
238
239 for (i=0; i < (sizeof(createStatements)/sizeof(createStatements[0])); ++i) {
240 log(LOG_SOMETIMES, "Creating tables. SQL command: %s", createStatements[i]);
241 res = sqlite3_exec(db,
242 createStatements[i],
243 NULL, /* No callback */
244 NULL, /* No callback closure */
245 NULL); /* error messages handled by sqlite_error */
246 if (res != SQLITE_OK)
247 sqlite_error(res, db, "Table creation failed: %s", createStatements[i]);
248 }
249}
250
251/* Populate the metadata "glue" tables event_kind and event_type. */
115 252
116#define EVENT_KIND_DO_INSERT(X, name, description) \ 253#define EVENT_KIND_DO_INSERT(X, name, description) \
117 res = sqlite3_bind_text(statement, 1, #name, -1, SQLITE_STATIC); \ 254 res = sqlite3_bind_text(statement, 1, #name, -1, SQLITE_STATIC); \
@@ -155,77 +292,227 @@ const char *populateStatements[] = {
155 if (res != SQLITE_OK) \ 292 if (res != SQLITE_OK) \
156 sqlite_error(res, db, "Couldn't reset event_type insert statement."); 293 sqlite_error(res, db, "Couldn't reset event_type insert statement.");
157 294
158static void fillTables(sqlite3 *db) 295static void fillGlueTables(sqlite3 *db)
159{ 296{
160 int i; 297 int i;
298 Res res;
161 sqlite3_stmt *statement; 299 sqlite3_stmt *statement;
162 int res; 300
163 301 statement = prepareStatement(db,
164 res = sqlite3_prepare_v2(db, 302 "INSERT OR IGNORE INTO event_kind (name, description, enum)"
165 "INSERT OR IGNORE INTO event_kind (name, description, enum)" 303 "VALUES (?, ?, ?)");
166 "VALUES (?, ?, ?)",
167 -1, /* prepare whole string as statement */
168 &statement,
169 NULL);
170 if (res != SQLITE_OK)
171 sqlite_error(res, db, "event_kind preparation failed");
172 304
173 i = 0; 305 i = 0;
174 EventKindENUM(EVENT_KIND_DO_INSERT, X); 306 EventKindENUM(EVENT_KIND_DO_INSERT, X);
175 307
176 res = sqlite3_finalize(statement); 308 finalizeStatement(db, statement);
177 if (res != SQLITE_OK)
178 sqlite_error(res, db, "event_kind finalize failed");
179
180 res = sqlite3_prepare_v2(db,
181 "INSERT OR IGNORE INTO event_type (name, code, always, kind)"
182 "VALUES (?, ?, ?, ?)",
183 -1, /* prepare whole string as statement */
184 &statement,
185 NULL);
186 if (res != SQLITE_OK)
187 sqlite_error(res, db, "event_type preparation failed");
188 309
310 statement = prepareStatement(db,
311 "INSERT OR IGNORE INTO event_type (name, code, always, kind)"
312 "VALUES (?, ?, ?, ?)");
189 EVENT_LIST(EVENT_TYPE_DO_INSERT, X); 313 EVENT_LIST(EVENT_TYPE_DO_INSERT, X);
190 314
191 res = sqlite3_finalize(statement); 315 finalizeStatement(db, statement);
192 if (res != SQLITE_OK)
193 sqlite_error(res, db, "event_type finalize failed");
194} 316}
195 317
196static void makeTables(sqlite3 *db) 318/* Populate the actual event tables. */
319
320#define EVENT_TYPE_DECLARE_STATEMENT(X, name, code, always, kind) \
321 sqlite3_stmt *stmt_##name;
322
323#define EVENT_PARAM_PREPARE_IDENT(X, index, sort, ident) "\"" #ident "\", "
324
325#define EVENT_PARAM_PREPARE_PLACE(X, index, sort, ident) "?, "
326
327#define EVENT_TYPE_PREPARE_STATEMENT(X, name, code, always, kind) \
328 stmt_##name = \
329 prepareStatement(db, \
330 "INSERT INTO EVENT_" #name " (" \
331 EVENT_##name##_PARAMS(EVENT_PARAM_PREPARE_IDENT, X) \
332 "log_serial, time) VALUES (" \
333 EVENT_##name##_PARAMS(EVENT_PARAM_PREPARE_PLACE,X) \
334 "?, ?)");
335
336#define EVENT_TYPE_FINALIZE_STATEMENT(X, name, code, always, kind) \
337 finalizeStatement(db, stmt_##name);
338
339#define EVENT_PARAM_BIND_INTEGER(name, index, sort, ident) \
340 res = sqlite3_bind_int64(statement, index+1, (unsigned long) event->name.f##index);
341
342#define EVENT_PARAM_BIND_REAL(name, index, sort, ident) \
343 res = sqlite3_bind_double(statement, index+1, event->name.f##index);
344
345#define EVENT_PARAM_BIND_TEXT(name, index, sort, ident) \
346 res = sqlite3_bind_text(statement, index+1, event->name.f##index, -1, SQLITE_STATIC);
347
348#define EVENT_PARAM_BIND_A EVENT_PARAM_BIND_INTEGER
349#define EVENT_PARAM_BIND_P EVENT_PARAM_BIND_INTEGER
350#define EVENT_PARAM_BIND_U EVENT_PARAM_BIND_INTEGER
351#define EVENT_PARAM_BIND_W EVENT_PARAM_BIND_INTEGER
352#define EVENT_PARAM_BIND_D EVENT_PARAM_BIND_REAL
353#define EVENT_PARAM_BIND_S EVENT_PARAM_BIND_TEXT
354#define EVENT_PARAM_BIND_B EVENT_PARAM_BIND_INTEGER
355
356#define EVENT_PARAM_BIND(name, index, sort, ident) \
357 EVENT_PARAM_BIND_##sort (name, index, sort, ident) \
358 if (res != SQLITE_OK) \
359 sqlite_error(res, db, "Event " #name " bind of ident " #ident "failed."); \
360 last_index = index+1;
361
362
363#define EVENT_TYPE_WRITE_SQL(X, name, code, always, kind) \
364 case code: \
365 { \
366 sqlite3_stmt *statement = stmt_##name; \
367 int last_index = 0; \
368 int res; \
369 /* bind all the parameters of this particular event with macro magic. */ \
370 EVENT_##name##_PARAMS(EVENT_PARAM_BIND, name) \
371 /* bind the fields we store for every event */ \
372 res = sqlite3_bind_int64(statement, last_index+1, log_serial); \
373 if (res != SQLITE_OK) \
374 sqlite_error(res, db, "Event " #name " bind of log_serial failed."); \
375 res = sqlite3_bind_int64(statement, last_index+2, clock); \
376 if (res != SQLITE_OK) \
377 sqlite_error(res, db, "Event " #name " bind of clock failed."); \
378 res = sqlite3_step(statement); \
379 if (res != SQLITE_DONE) \
380 sqlite_error(res, db, "insert of event \"" #name "\" failed."); \
381 res = sqlite3_reset(statement); \
382 if (res != SQLITE_OK) \
383 sqlite_error(res, db, "Couldn't reset insert statement for \"" #name "\"."); \
384 } \
385 break;
386
387/* readLog -- read and parse log
388 */
389
390static void readLog(EventProc proc,
391 sqlite3 *db)
197{ 392{
198 int i; 393 int log_serial = 0; /* TODO get this from event_log table */
199 int res; 394 size_t eventCount = 0;
200 395 /* declare statements for every event type */
201 for (i=0; i < (sizeof(createStatements)/sizeof(createStatements[0])); ++i) { 396 EVENT_LIST(EVENT_TYPE_DECLARE_STATEMENT, X);
202 log(LOG_SOMETIMES, "Creating tables. SQL command: %s", createStatements[i]); 397
203 res = sqlite3_exec(db, 398 /* prepare statements for every event type */
204 createStatements[i], 399 EVENT_LIST(EVENT_TYPE_PREPARE_STATEMENT, X);
205 NULL, /* No callback */ 400
206 NULL, /* No callback closure */ 401 while (TRUE) { /* loop for each event */
207 NULL); /* error messages handled by sqlite_error */ 402 Event event;
208 if (res != SQLITE_OK) 403 EventCode code;
209 sqlite_error(res, db, "Table creation failed: %s", createStatements[i]); 404 EventClock clock;
405 Res res;
406
407 /* Read and parse event. */
408 res = EventRead(&event, proc);
409 if (res == ResFAIL) break; /* eof */
410 if (res != ResOK) error("Truncated log");
411 clock = event->any.clock;
412 code = event->any.code;
413
414 /* Write event to SQLite. */
415 switch (code) {
416 EVENT_LIST(EVENT_TYPE_WRITE_SQL, X);
417 }
418 EventDestroy(proc, event);
419 eventCount++;
420 if ((eventCount % SMALL_TICK) == 0) {
421 fprintf(stdout, ".");
422 fflush(stdout);
423 if (((eventCount / SMALL_TICK) % BIG_TICK) == 0) {
424 log(LOG_OFTEN, "%lu events.", (unsigned long)eventCount);
425 }
426 }
210 } 427 }
428 log(LOG_OFTEN, "Wrote %lu events to SQL.", (unsigned long)eventCount);
429 /* finalize all the statements */
430 EVENT_LIST(EVENT_TYPE_FINALIZE_STATEMENT, X);
211} 431}
212 432
213void closeDatabase(sqlite3 *db) 433
434
435static Res logReader(void *file, void *p, size_t len)
214{ 436{
215 int res = sqlite3_close(db); 437 size_t n;
216 if (res != SQLITE_OK) 438
217 sqlite_error(res, db, "Closing database failed"); 439 n = fread(p, 1, len, (FILE *)file);
218 log(LOG_ALWAYS, "Closed database."); 440 return (n < len) ? (feof((FILE *)file) ? ResFAIL : ResIO) : ResOK;
441}
442
443#if 0
444/* TODO. Find out whether the database already contains this log file.
445 * Should probably also fopen it, and use fileno() and fstat() */
446
447static int logFileSerial(sqlite3 *db, const char *filename)
448{
449 struct stat st;
450 sqlite3_stmt *statement;
451
452 stat(filename, &st);
453 statement = prepareStatement(db,
454 "SELECT serial, completed FROM event_log WHERE"
455 "file_id = ? AND size = ? AND time_sec = ? AND time_nsec = ?");
456 /* TODO: Get stat results, look up in event_log, return accordingly */
457}
458#endif
459
460static FILE *openLog(void)
461{
462 const char *filename;
463 FILE *input;
464
465 filename = getenv(TELEMETRY_FILENAME_ENVAR);
466 if(filename == NULL)
467 filename = DEFAULT_TELEMETRY_FILENAME;
468 input = fopen(filename, "rb");
469
470 if (input == NULL)
471 error("unable to open %s", filename);
472
473 return input;
474}
475
476static void writeEventsToSQL(sqlite3 *db)
477{
478 Res res;
479 EventProc proc;
480 FILE *input;
481
482 input = openLog();
483 res = EventProcCreate(&proc, logReader, (void *)input);
484 if (res != ResOK)
485 error("Can't init EventProc module: error %d.", res);
486
487 readLog(proc, db);
488
489 EventProcDestroy(proc);
490 (void)fclose(input);
219} 491}
220 492
221 493
222int main(void) 494int main(void)
223{ 495{
224 sqlite3 *db; 496 sqlite3 *db;
497
498 /* TODO: command line args
499 * -v verbosity;
500 * -f log filename;
501 * -d database filename;
502 * -r rebuild glue tables (by dropping them);
503 * -t unit tests?
504 */
225 505
226 openDatabase(&db); 506 db = openDatabase();
227 makeTables(db); 507 makeTables(db);
228 fillTables(db); 508 fillGlueTables(db);
509
510 writeEventsToSQL(db);
511
512 if (0) {
513 testTableExists(db);
514 }
515
229 closeDatabase(db); 516 closeDatabase(db);
230 return 0; 517 return 0;
231} 518}