1/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3#ident "$Id$"
4/*======
5This file is part of PerconaFT.
6
7
8Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9
10 PerconaFT is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License, version 2,
12 as published by the Free Software Foundation.
13
14 PerconaFT is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
21
22----------------------------------------
23
24 PerconaFT is free software: you can redistribute it and/or modify
25 it under the terms of the GNU Affero General Public License, version 3,
26 as published by the Free Software Foundation.
27
28 PerconaFT is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU Affero General Public License for more details.
32
33 You should have received a copy of the GNU Affero General Public License
34 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
35======= */
36
37#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
38
39/* This file defines the logformat in an executable fashion.
40 * This code is used to generate
41 * The code that writes into the log.
42 * The code that reads the log and prints it to stdout (the log_print utility)
43 * The code that reads the log for recovery.
44 * The struct definitions.
45 * The Latex documentation.
46 */
47#include <ctype.h>
48#include <errno.h>
49#include <stdarg.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <sys/stat.h>
54#include <sys/types.h>
55#include <unistd.h>
56#include <toku_portability.h>
57#include <toku_assert.h>
58
59
60typedef struct field {
61 const char *type;
62 const char *name;
63 const char *format; // optional format string
64} F;
65
66#define NULLFIELD {0,0,0}
67#define FA (F[])
68
69enum log_begin_action {
70 IGNORE_LOG_BEGIN,
71 SHOULD_LOG_BEGIN,
72 ASSERT_BEGIN_WAS_LOGGED,
73 LOG_BEGIN_ACTION_NA = IGNORE_LOG_BEGIN
74};
75
76struct logtype {
77 const char *name;
78 unsigned int command_and_flags;
79 struct field *fields;
80 enum log_begin_action log_begin_action;
81};
82
83// In the fields, don't mention the command, the LSN, the CRC or the trailing LEN.
84
85const struct logtype rollbacks[] = {
86 //TODO: #2037 Add dname
87 {"fdelete", 'U', FA{{"FILENUM", "filenum", 0},
88 NULLFIELD}, LOG_BEGIN_ACTION_NA},
89 //TODO: #2037 Add dname
90 {"fcreate", 'F', FA{{"FILENUM", "filenum", 0},
91 {"BYTESTRING", "iname", 0},
92 NULLFIELD}, LOG_BEGIN_ACTION_NA},
93 //rename file
94 {"frename", 'n', FA{{"BYTESTRING", "old_iname", 0},
95 {"BYTESTRING", "new_iname", 0},
96 NULLFIELD}, LOG_BEGIN_ACTION_NA},
97 // cmdinsert is used to insert a key-value pair into a DB. For rollback we don't need the data.
98 {"cmdinsert", 'i', FA{
99 {"FILENUM", "filenum", 0},
100 {"BYTESTRING", "key", 0},
101 NULLFIELD}, LOG_BEGIN_ACTION_NA},
102 {"cmddelete", 'd', FA{
103 {"FILENUM", "filenum", 0},
104 {"BYTESTRING", "key", 0},
105 NULLFIELD}, LOG_BEGIN_ACTION_NA},
106 {"rollinclude", 'r', FA{{"TXNID_PAIR", "xid", 0},
107 {"uint64_t", "num_nodes", 0},
108 {"BLOCKNUM", "spilled_head", 0},
109 {"BLOCKNUM", "spilled_tail", 0},
110 NULLFIELD}, LOG_BEGIN_ACTION_NA},
111 {"load", 'l', FA{{"FILENUM", "old_filenum", 0},
112 {"BYTESTRING", "new_iname", 0},
113 NULLFIELD}, LOG_BEGIN_ACTION_NA},
114 // #2954
115 {"hot_index", 'h', FA{{"FILENUMS", "hot_index_filenums", 0},
116 NULLFIELD}, LOG_BEGIN_ACTION_NA},
117 {"dictionary_redirect", 'R', FA{{"FILENUM", "old_filenum", 0},
118 {"FILENUM", "new_filenum", 0},
119 NULLFIELD}, LOG_BEGIN_ACTION_NA},
120 {"cmdupdate", 'u', FA{{"FILENUM", "filenum", 0},
121 {"BYTESTRING", "key", 0},
122 NULLFIELD}, LOG_BEGIN_ACTION_NA},
123 {"cmdupdatebroadcast", 'B', FA{{"FILENUM", "filenum", 0},
124 {"bool", "is_resetting_op", 0},
125 NULLFIELD}, LOG_BEGIN_ACTION_NA},
126 {"change_fdescriptor", 'D', FA{{"FILENUM", "filenum", 0},
127 {"BYTESTRING", "old_descriptor", 0},
128 NULLFIELD}, LOG_BEGIN_ACTION_NA},
129 {0,0,FA{NULLFIELD}, LOG_BEGIN_ACTION_NA}
130};
131
132const struct logtype logtypes[] = {
133 // Records produced by checkpoints
134#if 0 // no longer used, but reserve the type
135 {"local_txn_checkpoint", 'c', FA{{"TXNID", "xid", 0}, NULLFIELD}},
136#endif
137 {"begin_checkpoint", 'x', FA{{"uint64_t", "timestamp", 0}, {"TXNID", "last_xid", 0}, NULLFIELD}, IGNORE_LOG_BEGIN},
138 {"end_checkpoint", 'X', FA{{"LSN", "lsn_begin_checkpoint", 0},
139 {"uint64_t", "timestamp", 0},
140 {"uint32_t", "num_fassociate_entries", 0}, // how many files were checkpointed
141 {"uint32_t", "num_xstillopen_entries", 0}, // how many txns were checkpointed
142 NULLFIELD}, IGNORE_LOG_BEGIN},
143 //TODO: #2037 Add dname
144 {"fassociate", 'f', FA{{"FILENUM", "filenum", 0},
145 {"uint32_t", "treeflags", 0},
146 {"BYTESTRING", "iname", 0}, // pathname of file
147 {"uint8_t", "unlink_on_close", 0},
148 NULLFIELD}, IGNORE_LOG_BEGIN},
149 //We do not use a txninfo struct since recovery log has
150 //FILENUMS and TOKUTXN has FTs (for open_fts)
151 {"xstillopen", 's', FA{{"TXNID_PAIR", "xid", 0},
152 {"TXNID_PAIR", "parentxid", 0},
153 {"uint64_t", "rollentry_raw_count", 0},
154 {"FILENUMS", "open_filenums", 0},
155 {"uint8_t", "force_fsync_on_commit", 0},
156 {"uint64_t", "num_rollback_nodes", 0},
157 {"uint64_t", "num_rollentries", 0},
158 {"BLOCKNUM", "spilled_rollback_head", 0},
159 {"BLOCKNUM", "spilled_rollback_tail", 0},
160 {"BLOCKNUM", "current_rollback", 0},
161 NULLFIELD}, ASSERT_BEGIN_WAS_LOGGED}, // record all transactions
162 // prepared txns need a gid
163 {"xstillopenprepared", 'p', FA{{"TXNID_PAIR", "xid", 0},
164 {"XIDP", "xa_xid", 0}, // prepared transactions need a gid, and have no parentxid.
165 {"uint64_t", "rollentry_raw_count", 0},
166 {"FILENUMS", "open_filenums", 0},
167 {"uint8_t", "force_fsync_on_commit", 0},
168 {"uint64_t", "num_rollback_nodes", 0},
169 {"uint64_t", "num_rollentries", 0},
170 {"BLOCKNUM", "spilled_rollback_head", 0},
171 {"BLOCKNUM", "spilled_rollback_tail", 0},
172 {"BLOCKNUM", "current_rollback", 0},
173 NULLFIELD}, ASSERT_BEGIN_WAS_LOGGED}, // record all transactions
174 // Records produced by transactions
175 {"xbegin", 'b', FA{{"TXNID_PAIR", "xid", 0},{"TXNID_PAIR", "parentxid", 0},NULLFIELD}, IGNORE_LOG_BEGIN},
176 {"xcommit",'C', FA{{"TXNID_PAIR", "xid", 0},NULLFIELD}, ASSERT_BEGIN_WAS_LOGGED},
177 {"xprepare",'P', FA{{"TXNID_PAIR", "xid", 0}, {"XIDP", "xa_xid", 0}, NULLFIELD}, ASSERT_BEGIN_WAS_LOGGED},
178 {"xabort", 'q', FA{{"TXNID_PAIR", "xid", 0},NULLFIELD}, ASSERT_BEGIN_WAS_LOGGED},
179 //TODO: #2037 Add dname
180 {"fcreate", 'F', FA{{"TXNID_PAIR", "xid", 0},
181 {"FILENUM", "filenum", 0},
182 {"BYTESTRING", "iname", 0},
183 {"uint32_t", "mode", "0%o"},
184 {"uint32_t", "treeflags", 0},
185 {"uint32_t", "nodesize", 0},
186 {"uint32_t", "basementnodesize", 0},
187 {"uint32_t", "compression_method", 0},
188 NULLFIELD}, SHOULD_LOG_BEGIN},
189 //TODO: #2037 Add dname
190 {"fopen", 'O', FA{{"BYTESTRING", "iname", 0},
191 {"FILENUM", "filenum", 0},
192 {"uint32_t", "treeflags", 0},
193 NULLFIELD}, IGNORE_LOG_BEGIN},
194 //TODO: #2037 Add dname
195 {"fclose", 'e', FA{{"BYTESTRING", "iname", 0},
196 {"FILENUM", "filenum", 0},
197 NULLFIELD}, IGNORE_LOG_BEGIN},
198 //TODO: #2037 Add dname
199 {"fdelete", 'U', FA{{"TXNID_PAIR", "xid", 0},
200 {"FILENUM", "filenum", 0},
201 NULLFIELD}, SHOULD_LOG_BEGIN},
202 {"frename", 'n', FA{{"TXNID_PAIR", "xid", 0},
203 {"BYTESTRING", "old_iname", 0},
204 {"FILENUM", "old_filenum", 0},
205 {"BYTESTRING", "new_iname", 0},
206 NULLFIELD}, IGNORE_LOG_BEGIN},
207 {"enq_insert", 'I', FA{{"FILENUM", "filenum", 0},
208 {"TXNID_PAIR", "xid", 0},
209 {"BYTESTRING", "key", 0},
210 {"BYTESTRING", "value", 0},
211 NULLFIELD}, SHOULD_LOG_BEGIN},
212 {"enq_insert_no_overwrite", 'i', FA{{"FILENUM", "filenum", 0},
213 {"TXNID_PAIR", "xid", 0},
214 {"BYTESTRING", "key", 0},
215 {"BYTESTRING", "value", 0},
216 NULLFIELD}, SHOULD_LOG_BEGIN},
217 {"enq_delete_any", 'E', FA{{"FILENUM", "filenum", 0},
218 {"TXNID_PAIR", "xid", 0},
219 {"BYTESTRING", "key", 0},
220 NULLFIELD}, SHOULD_LOG_BEGIN},
221 {"enq_insert_multiple", 'm', FA{{"FILENUM", "src_filenum", 0},
222 {"FILENUMS", "dest_filenums", 0},
223 {"TXNID_PAIR", "xid", 0},
224 {"BYTESTRING", "src_key", 0},
225 {"BYTESTRING", "src_val", 0},
226 NULLFIELD}, SHOULD_LOG_BEGIN},
227 {"enq_delete_multiple", 'M', FA{{"FILENUM", "src_filenum", 0},
228 {"FILENUMS", "dest_filenums", 0},
229 {"TXNID_PAIR", "xid", 0},
230 {"BYTESTRING", "src_key", 0},
231 {"BYTESTRING", "src_val", 0},
232 NULLFIELD}, SHOULD_LOG_BEGIN},
233 {"comment", 'T', FA{{"uint64_t", "timestamp", 0},
234 {"BYTESTRING", "comment", 0},
235 NULLFIELD}, IGNORE_LOG_BEGIN},
236 // Note: shutdown_up_to_19 log entry is NOT ALLOWED TO BE CHANGED.
237 // Do not change the letter ('Q'), do not add fields,
238 // do not remove fields.
239 // TODO: Kill this logentry entirely once we no longer support version 19.
240 {"shutdown_up_to_19", 'Q', FA{{"uint64_t", "timestamp", 0},
241 NULLFIELD}, IGNORE_LOG_BEGIN},
242 // Note: Shutdown log entry is NOT ALLOWED TO BE CHANGED.
243 // Do not change the letter ('0'), do not add fields,
244 // do not remove fields.
245 // You CAN leave this alone and add a new one, but then you have
246 // to deal with the upgrade mechanism again.
247 // This is how we detect clean shutdowns from OLDER VERSIONS.
248 // This log entry must always be readable for future versions.
249 // If you DO change it, you need to write a separate log upgrade mechanism.
250 {"shutdown", '0', FA{{"uint64_t", "timestamp", 0},
251 {"TXNID", "last_xid", 0},
252 NULLFIELD}, IGNORE_LOG_BEGIN},
253 {"load", 'l', FA{{"TXNID_PAIR", "xid", 0},
254 {"FILENUM", "old_filenum", 0},
255 {"BYTESTRING", "new_iname", 0},
256 NULLFIELD}, SHOULD_LOG_BEGIN},
257 // #2954
258 {"hot_index", 'h', FA{{"TXNID_PAIR", "xid", 0},
259 {"FILENUMS", "hot_index_filenums", 0},
260 NULLFIELD}, SHOULD_LOG_BEGIN},
261 {"enq_update", 'u', FA{{"FILENUM", "filenum", 0},
262 {"TXNID_PAIR", "xid", 0},
263 {"BYTESTRING", "key", 0},
264 {"BYTESTRING", "extra", 0},
265 NULLFIELD}, SHOULD_LOG_BEGIN},
266 {"enq_updatebroadcast", 'B', FA{{"FILENUM", "filenum", 0},
267 {"TXNID_PAIR", "xid", 0},
268 {"BYTESTRING", "extra", 0},
269 {"bool", "is_resetting_op", 0},
270 NULLFIELD}, SHOULD_LOG_BEGIN},
271 {"change_fdescriptor", 'D', FA{{"FILENUM", "filenum", 0},
272 {"TXNID_PAIR", "xid", 0},
273 {"BYTESTRING", "old_descriptor", 0},
274 {"BYTESTRING", "new_descriptor", 0},
275 {"bool", "update_cmp_descriptor", 0},
276 NULLFIELD}, SHOULD_LOG_BEGIN},
277 {0,0,FA{NULLFIELD}, (enum log_begin_action) 0}
278};
279
280
281#define DO_STRUCTS(lt, array, body) do { \
282 const struct logtype *lt; \
283 for (lt=&array[0]; lt->name; lt++) { \
284 body; \
285 } } while (0)
286
287#define DO_ROLLBACKS(lt, body) DO_STRUCTS(lt, rollbacks, body)
288
289#define DO_LOGTYPES(lt, body) DO_STRUCTS(lt, logtypes, body)
290
291#define DO_LOGTYPES_AND_ROLLBACKS(lt, body) (DO_ROLLBACKS(lt,body), DO_LOGTYPES(lt, body))
292
293#define DO_FIELDS(fld, lt, body) do { \
294 struct field *fld; \
295 for (fld=lt->fields; fld->type; fld++) { \
296 body; \
297 } } while (0)
298
299
300static void __attribute__((format (printf, 3, 4))) fprintf2 (FILE *f1, FILE *f2, const char *format, ...) {
301 va_list ap;
302 int r;
303 va_start(ap, format);
304 r=vfprintf(f1, format, ap); assert(r>=0);
305 va_end(ap);
306 va_start(ap, format);
307 r=vfprintf(f2, format, ap); assert(r>=0);
308 va_end(ap);
309}
310
311FILE *hf=0, *cf=0, *pf=0;
312
313static void
314generate_enum_internal (const char *enum_name, const char *enum_prefix, const struct logtype *lts) {
315 char used_cmds[256];
316 int count=0;
317 memset(used_cmds, 0, 256);
318 fprintf(hf, "enum %s {", enum_name);
319 DO_STRUCTS(lt, lts,
320 {
321 unsigned char cmd = (unsigned char)(lt->command_and_flags&0xff);
322 if (count!=0) fprintf(hf, ",");
323 count++;
324 fprintf(hf, "\n");
325 fprintf(hf," %s_%-16s = '%c'", enum_prefix, lt->name, cmd);
326 if (used_cmds[cmd]!=0) { fprintf(stderr, "%s:%d: error: Command %d (%c) was used twice (second time for %s)\n", __FILE__, __LINE__, cmd, cmd, lt->name); abort(); }
327 used_cmds[cmd]=1;
328 });
329 fprintf(hf, "\n};\n\n");
330
331}
332
333static void
334generate_enum (void) {
335 generate_enum_internal("lt_cmd", "LT", logtypes);
336 generate_enum_internal("rt_cmd", "RT", rollbacks);
337}
338
339static void
340generate_log_struct (void) {
341 DO_LOGTYPES(lt,
342 { fprintf(hf, "struct logtype_%s {\n", lt->name);
343 fprintf(hf, " %-16s lsn;\n", "LSN");
344 DO_FIELDS(field_type, lt,
345 fprintf(hf, " %-16s %s;\n", field_type->type, field_type->name));
346 fprintf(hf, " %-16s crc;\n", "uint32_t");
347 fprintf(hf, " %-16s len;\n", "uint32_t");
348 fprintf(hf, "};\n");
349 //fprintf(hf, "void toku_recover_%s (LSN lsn", lt->name);
350 //DO_FIELDS(field_type, lt, fprintf(hf, ", %s %s", field_type->type, field_type->name));
351 //fprintf(hf, ");\n");
352 });
353 DO_ROLLBACKS(lt,
354 { fprintf(hf, "struct rolltype_%s {\n", lt->name);
355 DO_FIELDS(field_type, lt,
356 fprintf(hf, " %-16s %s;\n", field_type->type, field_type->name));
357 fprintf(hf, "};\n");
358 fprintf(hf, "int toku_rollback_%s (", lt->name);
359 DO_FIELDS(field_type, lt, fprintf(hf, "%s %s,", field_type->type, field_type->name));
360 fprintf(hf, "TOKUTXN txn, LSN oplsn);\n");
361 fprintf(hf, "int toku_commit_%s (", lt->name);
362 DO_FIELDS(field_type, lt, fprintf(hf, "%s %s,", field_type->type, field_type->name));
363 fprintf(hf, "TOKUTXN txn, LSN oplsn);\n");
364 });
365 fprintf(hf, "struct log_entry {\n");
366 fprintf(hf, " enum lt_cmd cmd;\n");
367 fprintf(hf, " union {\n");
368 DO_LOGTYPES(lt, fprintf(hf," struct logtype_%s %s;\n", lt->name, lt->name));
369 fprintf(hf, " } u;\n");
370 fprintf(hf, "};\n");
371
372 fprintf(hf, "struct roll_entry {\n");
373 fprintf(hf, " enum rt_cmd cmd;\n");
374 fprintf(hf, " struct roll_entry *prev; /* for in-memory list of log entries. Threads from newest to oldest. */\n");
375 fprintf(hf, " union {\n");
376 DO_ROLLBACKS(lt, fprintf(hf," struct rolltype_%s %s;\n", lt->name, lt->name));
377 fprintf(hf, " } u;\n");
378 fprintf(hf, "};\n");
379
380}
381
382static void
383generate_dispatch (void) {
384 fprintf(hf, "#define rolltype_dispatch(s, funprefix) ({ switch((s)->cmd) {\\\n");
385 DO_ROLLBACKS(lt, fprintf(hf, " case RT_%s: funprefix ## %s (&(s)->u.%s); break;\\\n", lt->name, lt->name, lt->name));
386 fprintf(hf, " }})\n");
387
388 fprintf(hf, "#define logtype_dispatch_assign(s, funprefix, var, ...) do { switch((s)->cmd) {\\\n");
389 DO_LOGTYPES(lt, fprintf(hf, " case LT_%s: var = funprefix ## %s (&(s)->u.%s, __VA_ARGS__); break;\\\n", lt->name, lt->name, lt->name));
390 fprintf(hf, " }} while (0)\n");
391
392 fprintf(hf, "#define rolltype_dispatch_assign(s, funprefix, var, ...) do { \\\n");
393 fprintf(hf, " switch((s)->cmd) {\\\n");
394 DO_ROLLBACKS(lt, {
395 fprintf(hf, " case RT_%s: var = funprefix ## %s (", lt->name, lt->name);
396 int fieldcount=0;
397 DO_FIELDS(field_type, lt, {
398 if (fieldcount>0) fprintf(hf, ",");
399 fprintf(hf, "(s)->u.%s.%s", lt->name, field_type->name);
400 fieldcount++;
401 });
402 fprintf(hf, ", __VA_ARGS__); break;\\\n");
403 });
404 fprintf(hf, " default: assert(0);} } while (0)\n");
405
406 fprintf(hf, "#define logtype_dispatch_args(s, funprefix, ...) do { switch((s)->cmd) {\\\n");
407 DO_LOGTYPES(lt,
408 {
409 fprintf(hf, " case LT_%s: funprefix ## %s ((s)->u.%s.lsn", lt->name, lt->name, lt->name);
410 DO_FIELDS(field_type, lt, fprintf(hf, ",(s)->u.%s.%s", lt->name, field_type->name));
411 fprintf(hf, ", __VA_ARGS__); break;\\\n");
412 });
413 fprintf(hf, " }} while (0)\n");
414}
415
416static void
417generate_get_timestamp(void) {
418 fprintf(cf, "static uint64_t toku_get_timestamp(void) {\n");
419 fprintf(cf, " struct timeval tv; int r = gettimeofday(&tv, NULL);\n");
420 fprintf(cf, " assert(r==0);\n");
421 fprintf(cf, " return tv.tv_sec * 1000000ULL + tv.tv_usec;\n");
422 fprintf(cf, "}\n");
423}
424
425static void
426generate_log_writer (void) {
427 generate_get_timestamp();
428 DO_LOGTYPES(lt, {
429 //TODO(yoni): The overhead variables are NOT correct for BYTESTRING, FILENUMS (or any other variable length type)
430 // We should switch to something like using toku_logsizeof_*.
431 fprintf(hf, "static const size_t toku_log_%s_overhead = (+4+1+8", lt->name);
432 DO_FIELDS(field_type, lt, fprintf(hf, "+sizeof(%s)", field_type->type));
433 fprintf(hf, "+8);\n");
434 fprintf2(cf, hf, "void toku_log_%s (TOKULOGGER logger, LSN *lsnp, int do_fsync", lt->name);
435 switch (lt->log_begin_action) {
436 case SHOULD_LOG_BEGIN:
437 case ASSERT_BEGIN_WAS_LOGGED: {
438 fprintf2(cf, hf, ", TOKUTXN txn");
439 break;
440 }
441 case IGNORE_LOG_BEGIN: break;
442 }
443 DO_FIELDS(field_type, lt, fprintf2(cf, hf, ", %s %s", field_type->type, field_type->name));
444 fprintf(hf, ");\n");
445 fprintf(cf, ") {\n");
446 fprintf(cf, " if (logger == NULL) {\n");
447 fprintf(cf, " return;\n");
448 fprintf(cf, " }\n");
449 switch (lt->log_begin_action) {
450 case SHOULD_LOG_BEGIN: {
451 fprintf(cf, " //txn can be NULL during tests\n");
452 fprintf(cf, " //never null when not checkpoint.\n");
453 fprintf(cf, " if (txn && !txn->begin_was_logged) {\n");
454 fprintf(cf, " invariant(!txn_declared_read_only(txn));\n");
455 fprintf(cf, " toku_maybe_log_begin_txn_for_write_operation(txn);\n");
456 fprintf(cf, " }\n");
457 break;
458 }
459 case ASSERT_BEGIN_WAS_LOGGED: {
460 fprintf(cf, " //txn can be NULL during tests\n");
461 fprintf(cf, " invariant(!txn || txn->begin_was_logged);\n");
462 fprintf(cf, " invariant(!txn || !txn_declared_read_only(txn));\n");
463 break;
464 }
465 case IGNORE_LOG_BEGIN: break;
466 }
467 fprintf(cf, " if (!logger->write_log_files) {\n");
468 fprintf(cf, " ml_lock(&logger->input_lock);\n");
469 fprintf(cf, " logger->lsn.lsn++;\n");
470 fprintf(cf, " if (lsnp) *lsnp=logger->lsn;\n");
471 fprintf(cf, " ml_unlock(&logger->input_lock);\n");
472 fprintf(cf, " return;\n");
473 fprintf(cf, " }\n");
474 fprintf(cf, " const unsigned int buflen= (+4 // len at the beginning\n");
475 fprintf(cf, " +1 // log command\n");
476 fprintf(cf, " +8 // lsn\n");
477 DO_FIELDS(field_type, lt,
478 fprintf(cf, " +toku_logsizeof_%s(%s)\n", field_type->type, field_type->name));
479 fprintf(cf, " +8 // crc + len\n");
480 fprintf(cf, " );\n");
481 fprintf(cf, " struct wbuf wbuf;\n");
482 fprintf(cf, " ml_lock(&logger->input_lock);\n");
483 fprintf(cf, " toku_logger_make_space_in_inbuf(logger, buflen);\n");
484 fprintf(cf, " wbuf_nocrc_init(&wbuf, logger->inbuf.buf+logger->inbuf.n_in_buf, buflen);\n");
485 fprintf(cf, " wbuf_nocrc_int(&wbuf, buflen);\n");
486 fprintf(cf, " wbuf_nocrc_char(&wbuf, '%c');\n", (char)(0xff&lt->command_and_flags));
487 fprintf(cf, " logger->lsn.lsn++;\n");
488 fprintf(cf, " logger->inbuf.max_lsn_in_buf = logger->lsn;\n");
489 fprintf(cf, " wbuf_nocrc_LSN(&wbuf, logger->lsn);\n");
490 fprintf(cf, " if (lsnp) *lsnp=logger->lsn;\n");
491 DO_FIELDS(field_type, lt,
492 if (strcmp(field_type->name, "timestamp") == 0)
493 fprintf(cf, " if (timestamp == 0) timestamp = toku_get_timestamp();\n");
494 fprintf(cf, " wbuf_nocrc_%s(&wbuf, %s);\n", field_type->type, field_type->name));
495 fprintf(cf, " wbuf_nocrc_int(&wbuf, toku_x1764_memory(wbuf.buf, wbuf.ndone));\n");
496 fprintf(cf, " wbuf_nocrc_int(&wbuf, buflen);\n");
497 fprintf(cf, " assert(wbuf.ndone==buflen);\n");
498 fprintf(cf, " logger->inbuf.n_in_buf += buflen;\n");
499 fprintf(cf, " toku_logger_maybe_fsync(logger, logger->lsn, do_fsync, true);\n");
500 fprintf(cf, "}\n\n");
501 });
502}
503
504static void
505generate_log_reader (void) {
506 DO_LOGTYPES(lt, {
507 fprintf(cf, "static int toku_log_fread_%s (FILE *infile, uint32_t len1, struct logtype_%s *data, struct x1764 *checksum)", lt->name, lt->name);
508 fprintf(cf, " {\n");
509 fprintf(cf, " int r=0;\n");
510 fprintf(cf, " uint32_t actual_len=5; // 1 for the command, 4 for the first len.\n");
511 fprintf(cf, " r=toku_fread_%-16s(infile, &data->%-16s, checksum, &actual_len); if (r!=0) return r;\n", "LSN", "lsn");
512 DO_FIELDS(field_type, lt,
513 fprintf(cf, " r=toku_fread_%-16s(infile, &data->%-16s, checksum, &actual_len); if (r!=0) return r;\n", field_type->type, field_type->name));
514 fprintf(cf, " uint32_t checksum_in_file, len_in_file;\n");
515 fprintf(cf, " r=toku_fread_uint32_t_nocrclen(infile, &checksum_in_file); actual_len+=4; if (r!=0) return r;\n");
516 fprintf(cf, " r=toku_fread_uint32_t_nocrclen(infile, &len_in_file); actual_len+=4; if (r!=0) return r;\n");
517 fprintf(cf, " if (checksum_in_file!=toku_x1764_finish(checksum) || len_in_file!=actual_len || len1 != len_in_file) return DB_BADFORMAT;\n");
518 fprintf(cf, " return 0;\n");
519 fprintf(cf, "}\n\n");
520 });
521 fprintf2(cf, hf, "int toku_log_fread (FILE *infile, struct log_entry *le)");
522 fprintf(hf, ";\n");
523 fprintf(cf, " {\n");
524 fprintf(cf, " uint32_t len1; int r;\n");
525 fprintf(cf, " uint32_t ignorelen=0;\n");
526 fprintf(cf, " struct x1764 checksum;\n");
527 fprintf(cf, " toku_x1764_init(&checksum);\n");
528 fprintf(cf, " r = toku_fread_uint32_t(infile, &len1, &checksum, &ignorelen); if (r!=0) return r;\n");
529 fprintf(cf, " int cmd=fgetc(infile);\n");
530 fprintf(cf, " if (cmd==EOF) return EOF;\n");
531 fprintf(cf, " char cmdchar = (char)cmd;\n");
532 fprintf(cf, " toku_x1764_add(&checksum, &cmdchar, 1);\n");
533 fprintf(cf, " le->cmd=(enum lt_cmd)cmd;\n");
534 fprintf(cf, " switch ((enum lt_cmd)cmd) {\n");
535 DO_LOGTYPES(lt, {
536 fprintf(cf, " case LT_%s:\n", lt->name);
537 fprintf(cf, " return toku_log_fread_%s (infile, len1, &le->u.%s, &checksum);\n", lt->name, lt->name);
538 });
539 fprintf(cf, " };\n");
540 fprintf(cf, " return DB_BADFORMAT;\n"); // Should read past the record using the len field.
541 fprintf(cf, "}\n\n");
542 //fprintf2(cf, hf, "// Return 0 if there is something to read, return -1 if nothing to read, abort if an error.\n");
543 fprintf2(cf, hf, "// Return 0 if there is something to read, -1 if nothing to read, >0 on error\n");
544 fprintf2(cf, hf, "int toku_log_fread_backward (FILE *infile, struct log_entry *le)");
545 fprintf(hf, ";\n");
546 fprintf(cf, "{\n");
547 fprintf(cf, " memset(le, 0, sizeof(*le));\n");
548 fprintf(cf, " long pos = ftell(infile);\n");
549 fprintf(cf, " if (pos<=12) return -1;\n");
550 fprintf(cf, " int r = fseek(infile, -4, SEEK_CUR); \n");// assert(r==0);\n");
551 fprintf(cf, " if (r!=0) return get_error_errno();\n");
552 fprintf(cf, " uint32_t len;\n");
553 fprintf(cf, " r = toku_fread_uint32_t_nocrclen(infile, &len); \n");// assert(r==0);\n");
554 fprintf(cf, " if (r!=0) return 1;\n");
555 fprintf(cf, " r = fseek(infile, -(int)len, SEEK_CUR) ; \n");// assert(r==0);\n");
556 fprintf(cf, " if (r!=0) return get_error_errno();\n");
557 fprintf(cf, " r = toku_log_fread(infile, le); \n");// assert(r==0);\n");
558 fprintf(cf, " if (r!=0) return 1;\n");
559 fprintf(cf, " long afterpos = ftell(infile);\n");
560 fprintf(cf, " if (afterpos != pos) return 1;\n");
561 fprintf(cf, " r = fseek(infile, -(int)len, SEEK_CUR); \n");// assert(r==0);\n");
562 fprintf(cf, " if (r!=0) return get_error_errno();\n");
563 fprintf(cf, " return 0;\n");
564 fprintf(cf, "}\n\n");
565
566 DO_LOGTYPES(lt, ({
567 fprintf(cf, "static void toku_log_free_log_entry_%s_resources (struct logtype_%s *data", lt->name, lt->name);
568 if (!lt->fields->type) fprintf(cf, " __attribute__((__unused__))");
569 fprintf(cf, ") {\n");
570 DO_FIELDS(field_type, lt,
571 fprintf(cf, " toku_free_%s(data->%s);\n", field_type->type, field_type->name);
572 );
573 fprintf(cf, "}\n\n");
574 }));
575 fprintf2(cf, hf, "void toku_log_free_log_entry_resources (struct log_entry *le)");
576 fprintf(hf, ";\n");
577 fprintf(cf, " {\n");
578 fprintf(cf, " switch ((enum lt_cmd)le->cmd) {\n");
579 DO_LOGTYPES(lt, {
580 fprintf(cf, " case LT_%s:\n", lt->name);
581 fprintf(cf, " return toku_log_free_log_entry_%s_resources (&(le->u.%s));\n", lt->name, lt->name);
582 });
583 fprintf(cf, " };\n");
584 fprintf(cf, " return;\n");
585 fprintf(cf, "}\n\n");
586}
587
588static void
589generate_logprint (void) {
590 unsigned maxnamelen=0;
591 fprintf2(pf, hf, "int toku_logprint_one_record(FILE *outf, FILE *f)");
592 fprintf(hf, ";\n");
593 fprintf(pf, " {\n");
594 fprintf(pf, " int cmd, r;\n");
595 fprintf(pf, " uint32_t len1, crc_in_file;\n");
596 fprintf(pf, " uint32_t ignorelen=0;\n");
597 fprintf(pf, " struct x1764 checksum;\n");
598 fprintf(pf, " toku_x1764_init(&checksum);\n");
599 fprintf(pf, " r=toku_fread_uint32_t(f, &len1, &checksum, &ignorelen);\n");
600 fprintf(pf, " if (r==EOF) return EOF;\n");
601 fprintf(pf, " cmd=fgetc(f);\n");
602 fprintf(pf, " if (cmd==EOF) return DB_BADFORMAT;\n");
603 fprintf(pf, " uint32_t len_in_file, len=1+4; // cmd + len1\n");
604 fprintf(pf, " char charcmd = (char)cmd;\n");
605 fprintf(pf, " toku_x1764_add(&checksum, &charcmd, 1);\n");
606 fprintf(pf, " switch ((enum lt_cmd)cmd) {\n");
607 DO_LOGTYPES(lt, { if (strlen(lt->name)>maxnamelen) maxnamelen=strlen(lt->name); });
608 DO_LOGTYPES(lt, {
609 unsigned char cmd = (unsigned char)(0xff&lt->command_and_flags);
610 fprintf(pf, " case LT_%s: \n", lt->name);
611 // We aren't using the log reader here because we want better diagnostics as soon as things go wrong.
612 fprintf(pf, " fprintf(outf, \"%%-%us \", \"%s\");\n", maxnamelen, lt->name);
613 if (isprint(cmd)) fprintf(pf," fprintf(outf, \" '%c':\");\n", cmd);
614 else fprintf(pf," fprintf(outf, \"0%03o:\");\n", cmd);
615 fprintf(pf, " r = toku_logprint_%-16s(outf, f, \"lsn\", &checksum, &len, 0); if (r!=0) return r;\n", "LSN");
616 DO_FIELDS(field_type, lt, {
617 fprintf(pf, " r = toku_logprint_%-16s(outf, f, \"%s\", &checksum, &len,", field_type->type, field_type->name);
618 if (field_type->format) fprintf(pf, "\"%s\"", field_type->format);
619 else fprintf(pf, "0");
620 fprintf(pf, "); if (r!=0) return r;\n");
621 });
622 fprintf(pf, " {\n");
623 fprintf(pf, " uint32_t actual_murmur = toku_x1764_finish(&checksum);\n");
624 fprintf(pf, " r = toku_fread_uint32_t_nocrclen (f, &crc_in_file); len+=4; if (r!=0) return r;\n");
625 fprintf(pf, " fprintf(outf, \" crc=%%08x\", crc_in_file);\n");
626 fprintf(pf, " if (crc_in_file!=actual_murmur) fprintf(outf, \" checksum=%%08x\", actual_murmur);\n");
627 fprintf(pf, " r = toku_fread_uint32_t_nocrclen (f, &len_in_file); len+=4; if (r!=0) return r;\n");
628 fprintf(pf, " fprintf(outf, \" len=%%u\", len_in_file);\n");
629 fprintf(pf, " if (len_in_file!=len) fprintf(outf, \" actual_len=%%u\", len);\n");
630 fprintf(pf, " if (len_in_file!=len || crc_in_file!=actual_murmur) return DB_BADFORMAT;\n");
631 fprintf(pf, " };\n");
632 fprintf(pf, " fprintf(outf, \"\\n\");\n");
633 fprintf(pf, " return 0;\n\n");
634 });
635 fprintf(pf, " }\n");
636 fprintf(pf, " fprintf(outf, \"Unknown command %%d ('%%c')\", cmd, cmd);\n");
637 fprintf(pf, " return DB_BADFORMAT;\n");
638 fprintf(pf, "}\n\n");
639}
640
641static void
642generate_rollbacks (void) {
643 DO_ROLLBACKS(lt, {
644 fprintf2(cf, hf, "void toku_logger_save_rollback_%s (TOKUTXN txn", lt->name);
645 DO_FIELDS(field_type, lt, {
646 if ( strcmp(field_type->type, "BYTESTRING") == 0 ) {
647 fprintf2(cf, hf, ", BYTESTRING *%s_ptr", field_type->name);
648 }
649 else if ( strcmp(field_type->type, "FILENUMS") == 0 ) {
650 fprintf2(cf, hf, ", FILENUMS *%s_ptr", field_type->name);
651 }
652 else {
653 fprintf2(cf, hf, ", %s %s", field_type->type, field_type->name);
654 }
655 });
656
657 fprintf(hf, ");\n");
658 fprintf(cf, ") {\n");
659 fprintf(cf, " toku_txn_lock(txn);\n");
660 fprintf(cf, " ROLLBACK_LOG_NODE log;\n");
661 fprintf(cf, " toku_get_and_pin_rollback_log_for_new_entry(txn, &log);\n");
662 // 'memdup' all BYTESTRINGS here
663 DO_FIELDS(field_type, lt, {
664 if ( strcmp(field_type->type, "BYTESTRING") == 0 ) {
665 fprintf(cf, " BYTESTRING %s = {\n"
666 " .len = %s_ptr->len,\n"
667 " .data = cast_to_typeof(%s.data) toku_memdup_in_rollback(log, %s_ptr->data, %s_ptr->len)\n"
668 " };\n",
669 field_type->name, field_type->name, field_type->name, field_type->name, field_type->name);
670 }
671 if ( strcmp(field_type->type, "FILENUMS") == 0 ) {
672 fprintf(cf, " FILENUMS %s = {\n"
673 " .num = %s_ptr->num,\n"
674 " .filenums = cast_to_typeof(%s.filenums) toku_memdup_in_rollback(log, %s_ptr->filenums, %s_ptr->num * (sizeof (FILENUM)))\n"
675 " };\n",
676 field_type->name, field_type->name, field_type->name, field_type->name, field_type->name);
677 }
678 });
679 {
680 int count=0;
681 fprintf(cf, " uint32_t rollback_fsize = toku_logger_rollback_fsize_%s(", lt->name);
682 DO_FIELDS(field_type, lt, fprintf(cf, "%s%s", (count++>0)?", ":"", field_type->name));
683 fprintf(cf, ");\n");
684 }
685 fprintf(cf, " struct roll_entry *v;\n");
686 fprintf(cf, " size_t mem_needed = sizeof(v->u.%s) + __builtin_offsetof(struct roll_entry, u.%s);\n", lt->name, lt->name);
687 fprintf(cf, " CAST_FROM_VOIDP(v, toku_malloc_in_rollback(log, mem_needed));\n");
688 fprintf(cf, " assert(v);\n");
689 fprintf(cf, " v->cmd = (enum rt_cmd)%u;\n", lt->command_and_flags&0xff);
690 DO_FIELDS(field_type, lt, fprintf(cf, " v->u.%s.%s = %s;\n", lt->name, field_type->name, field_type->name));
691 fprintf(cf, " v->prev = log->newest_logentry;\n");
692 fprintf(cf, " if (log->oldest_logentry==NULL) log->oldest_logentry=v;\n");
693 fprintf(cf, " log->newest_logentry = v;\n");
694 fprintf(cf, " log->rollentry_resident_bytecount += rollback_fsize;\n");
695 fprintf(cf, " txn->roll_info.rollentry_raw_count += rollback_fsize;\n");
696 fprintf(cf, " txn->roll_info.num_rollentries++;\n");
697 fprintf(cf, " log->dirty = true;\n");
698 fprintf(cf, " // spill and unpin assert success internally\n");
699 fprintf(cf, " toku_maybe_spill_rollbacks(txn, log);\n");
700 fprintf(cf, " toku_rollback_log_unpin(txn, log);\n");
701 fprintf(cf, " toku_txn_unlock(txn);\n");
702 fprintf(cf, "}\n");
703 });
704
705 DO_ROLLBACKS(lt, {
706 fprintf2(cf, hf, "void toku_logger_rollback_wbuf_nocrc_write_%s (struct wbuf *wbuf", lt->name);
707 DO_FIELDS(field_type, lt, fprintf2(cf, hf, ", %s %s", field_type->type, field_type->name));
708 fprintf2(cf, hf, ")");
709 fprintf(hf, ";\n");
710 fprintf(cf, " {\n");
711
712 {
713 int count=0;
714 fprintf(cf, " uint32_t rollback_fsize = toku_logger_rollback_fsize_%s(", lt->name);
715 DO_FIELDS(field_type, lt, fprintf(cf, "%s%s", (count++>0)?", ":"", field_type->name));
716 fprintf(cf, ");\n");
717 fprintf(cf, " wbuf_nocrc_int(wbuf, rollback_fsize);\n");
718 }
719 fprintf(cf, " wbuf_nocrc_char(wbuf, '%c');\n", (char)(0xff&lt->command_and_flags));
720 DO_FIELDS(field_type, lt, fprintf(cf, " wbuf_nocrc_%s(wbuf, %s);\n", field_type->type, field_type->name));
721 fprintf(cf, "}\n");
722 });
723 fprintf2(cf, hf, "void toku_logger_rollback_wbuf_nocrc_write (struct wbuf *wbuf, struct roll_entry *r)");
724 fprintf(hf, ";\n");
725 fprintf(cf, " {\n switch (r->cmd) {\n");
726 DO_ROLLBACKS(lt, {
727 fprintf(cf, " case RT_%s: toku_logger_rollback_wbuf_nocrc_write_%s(wbuf", lt->name, lt->name);
728 DO_FIELDS(field_type, lt, fprintf(cf, ", r->u.%s.%s", lt->name, field_type->name));
729 fprintf(cf, "); return;\n");
730 });
731 fprintf(cf, " }\n assert(0);\n");
732 fprintf(cf, "}\n");
733 DO_ROLLBACKS(lt, {
734 fprintf2(cf, hf, "uint32_t toku_logger_rollback_fsize_%s (", lt->name);
735 int count=0;
736 DO_FIELDS(field_type, lt, fprintf2(cf, hf, "%s%s %s", (count++>0)?", ":"", field_type->type, field_type->name));
737 fprintf(hf, ");\n");
738 fprintf(cf, ") {\n");
739 fprintf(cf, " return 1 /* the cmd*/\n");
740 fprintf(cf, " + 4 /* the int at the end saying the size */");
741 DO_FIELDS(field_type, lt,
742 fprintf(cf, "\n + toku_logsizeof_%s(%s)", field_type->type, field_type->name));
743 fprintf(cf, ";\n}\n");
744 });
745 fprintf2(cf, hf, "uint32_t toku_logger_rollback_fsize(struct roll_entry *item)");
746 fprintf(hf, ";\n");
747 fprintf(cf, "{\n switch(item->cmd) {\n");
748 DO_ROLLBACKS(lt, {
749 fprintf(cf, " case RT_%s: return toku_logger_rollback_fsize_%s(", lt->name, lt->name);
750 int count=0;
751 DO_FIELDS(field_type, lt, fprintf(cf, "%sitem->u.%s.%s", (count++>0)?", ":"", lt->name, field_type->name));
752 fprintf(cf, ");\n");
753 });
754 fprintf(cf, " }\n assert(0);\n return 0;\n");
755 fprintf(cf, "}\n");
756
757 fprintf2(cf, hf, "int toku_parse_rollback(unsigned char *buf, uint32_t n_bytes, struct roll_entry **itemp, memarena *ma)");
758 fprintf(hf, ";\n");
759 fprintf(cf, " {\n assert(n_bytes>0);\n struct roll_entry *item;\n enum rt_cmd cmd = (enum rt_cmd)(buf[0]);\n size_t mem_needed;\n");
760 fprintf(cf, " struct rbuf rc = {buf, n_bytes, 1};\n");
761 fprintf(cf, " switch(cmd) {\n");
762 DO_ROLLBACKS(lt, {
763 fprintf(cf, " case RT_%s:\n", lt->name);
764 fprintf(cf, " mem_needed = sizeof(item->u.%s) + __builtin_offsetof(struct roll_entry, u.%s);\n", lt->name, lt->name);
765 fprintf(cf, " CAST_FROM_VOIDP(item, ma->malloc_from_arena(mem_needed));\n");
766 fprintf(cf, " item->cmd = cmd;\n");
767 DO_FIELDS(field_type, lt, fprintf(cf, " rbuf_ma_%s(&rc, ma, &item->u.%s.%s);\n", field_type->type, lt->name, field_type->name));
768 fprintf(cf, " *itemp = item;\n");
769 fprintf(cf, " return 0;\n");
770 });
771 fprintf(cf, " }\n return EINVAL;\n}\n");
772}
773
774static void
775generate_log_entry_functions(void) {
776 fprintf(hf, "LSN toku_log_entry_get_lsn(struct log_entry *);\n");
777 fprintf(cf, "LSN toku_log_entry_get_lsn(struct log_entry *le) {\n");
778 fprintf(cf, " return le->u.begin_checkpoint.lsn;\n");
779 fprintf(cf, "}\n");
780}
781
782const char codefile[] = "log_code.cc";
783const char printfile[] = "log_print.cc";
784const char headerfile[] = "log_header.h";
785int main (int argc, const char *const argv[]) {
786 assert(argc==2); // the single argument is the directory into which to put things
787 const char *dir = argv[1];
788 size_t codepathlen = sizeof(codefile) + strlen(dir) + 4;
789 size_t printpathlen = sizeof(printfile) + strlen(dir) + 4;
790 size_t headerpathlen = sizeof(headerfile) + strlen(dir) + 4;
791 char codepath[codepathlen];
792 char printpath[printpathlen];
793 char headerpath[headerpathlen];
794 { int r = snprintf(codepath, codepathlen, "%s/%s", argv[1], codefile); assert(r<(int)codepathlen); }
795 { int r = snprintf(printpath, printpathlen, "%s/%s", argv[1], printfile); assert(r<(int)printpathlen); }
796 { int r = snprintf(headerpath, headerpathlen, "%s/%s", argv[1], headerfile); assert(r<(int)headerpathlen); }
797 chmod(codepath, S_IRUSR|S_IWUSR);
798 chmod(headerpath, S_IRUSR|S_IWUSR);
799 unlink(codepath);
800 unlink(headerpath);
801 cf = fopen(codepath, "w");
802 if (cf==0) { int r = get_error_errno(); printf("fopen of %s failed because of errno=%d (%s)\n", codepath, r, strerror(r)); } // sometimes this is failing, so let's make a better diagnostic
803 assert(cf!=0);
804 hf = fopen(headerpath, "w"); assert(hf!=0);
805 pf = fopen(printpath, "w"); assert(pf!=0);
806 fprintf2(cf, hf, "/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */\n");
807 fprintf2(cf, hf, "// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:\n");
808 fprintf(hf, "#pragma once\n");
809 fprintf2(cf, hf, "/* Do not edit this file. This code generated by logformat.c. Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved. */\n");
810 fprintf2(cf, hf, "#ident \"Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.\"\n");
811 fprintf2(cf, pf, "#include <stdint.h>\n");
812 fprintf2(cf, pf, "#include <sys/time.h>\n");
813 fprintf2(cf, pf, "#include <ft/logger/log-internal.h>\n");
814 fprintf(hf, "#include <ft/ft-internal.h>\n");
815 fprintf(hf, "#include <util/bytestring.h>\n");
816 fprintf(hf, "#include <util/memarena.h>\n");
817 generate_enum();
818 generate_log_struct();
819 generate_dispatch();
820 generate_log_writer();
821 generate_log_reader();
822 generate_rollbacks();
823 generate_log_entry_functions();
824 generate_logprint();
825 {
826 int r=fclose(hf); assert(r==0);
827 r=fclose(cf); assert(r==0);
828 r=fclose(pf); assert(r==0);
829 // Make it tougher to modify by mistake
830 chmod(codepath, S_IRUSR|S_IRGRP|S_IROTH);
831 chmod(headerpath, S_IRUSR|S_IRGRP|S_IROTH);
832 }
833 return 0;
834}
835
836