namespace the int functions
[babeltrace.git] / formats / ctf / events.c
1 /*
2 * ctf/events.c
3 *
4 * Babeltrace Library
5 *
6 * Copyright 2011-2012 EfficiOS Inc. and Linux Foundation
7 *
8 * Author: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
9 * Julien Desfossez <julien.desfossez@efficios.com>
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a copy
12 * of this software and associated documentation files (the "Software"), to deal
13 * in the Software without restriction, including without limitation the rights
14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 * copies of the Software, and to permit persons to whom the Software is
16 * furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * SOFTWARE.
28 */
29
30 #include <babeltrace/babeltrace.h>
31 #include <babeltrace/format.h>
32 #include <babeltrace/ctf/events.h>
33 #include <babeltrace/ctf-ir/metadata.h>
34 #include <babeltrace/prio_heap.h>
35 #include <babeltrace/iterator-internal.h>
36 #include <babeltrace/ctf/events-internal.h>
37 #include <babeltrace/ctf/metadata.h>
38 #include <glib.h>
39
40 #include "events-private.h"
41
42 /*
43 * thread local storage to store the last error that occured
44 * while reading a field, this variable must be accessed by
45 * bt_ctf_field_get_error only
46 */
47 __thread int bt_ctf_last_field_error = 0;
48
49 const struct definition *bt_ctf_get_top_level_scope(const struct bt_ctf_event *ctf_event,
50 enum bt_ctf_scope scope)
51 {
52 const struct definition *tmp = NULL;
53 const struct ctf_event_definition *event;
54
55 if (!ctf_event)
56 return NULL;
57
58 event = ctf_event->parent;
59 switch (scope) {
60 case BT_TRACE_PACKET_HEADER:
61 if (!event->stream)
62 goto error;
63 if (event->stream->trace_packet_header)
64 tmp = &event->stream->trace_packet_header->p;
65 break;
66 case BT_STREAM_PACKET_CONTEXT:
67 if (!event->stream)
68 goto error;
69 if (event->stream->stream_packet_context)
70 tmp = &event->stream->stream_packet_context->p;
71 break;
72 case BT_STREAM_EVENT_HEADER:
73 if (!event->stream)
74 goto error;
75 if (event->stream->stream_event_header)
76 tmp = &event->stream->stream_event_header->p;
77 break;
78 case BT_STREAM_EVENT_CONTEXT:
79 if (!event->stream)
80 goto error;
81 if (event->stream->stream_event_context)
82 tmp = &event->stream->stream_event_context->p;
83 break;
84 case BT_EVENT_CONTEXT:
85 if (event->event_context)
86 tmp = &event->event_context->p;
87 break;
88 case BT_EVENT_FIELDS:
89 if (event->event_fields)
90 tmp = &event->event_fields->p;
91 break;
92 }
93 return tmp;
94
95 error:
96 return NULL;
97 }
98
99 const struct definition *bt_ctf_get_field(const struct bt_ctf_event *ctf_event,
100 const struct definition *scope,
101 const char *field)
102 {
103 const struct definition *def;
104 char *field_underscore;
105
106 if (!ctf_event || !scope || !field)
107 return NULL;
108
109 def = lookup_definition(scope, field);
110 /*
111 * optionally a field can have an underscore prefix, try
112 * to lookup the field with this prefix if it failed
113 */
114 if (!def) {
115 field_underscore = g_new(char, strlen(field) + 2);
116 field_underscore[0] = '_';
117 strcpy(&field_underscore[1], field);
118 def = lookup_definition(scope, field_underscore);
119 g_free(field_underscore);
120 }
121 if (bt_ctf_field_type(bt_ctf_get_decl_from_def(def)) == CTF_TYPE_VARIANT) {
122 const struct definition_variant *variant_definition;
123 variant_definition = container_of(def,
124 const struct definition_variant, p);
125 return variant_definition->current_field;
126 }
127 return def;
128 }
129
130 const struct definition *bt_ctf_get_index(const struct bt_ctf_event *ctf_event,
131 const struct definition *field,
132 unsigned int index)
133 {
134 struct definition *ret = NULL;
135
136 if (!ctf_event || !field)
137 return NULL;
138
139 if (bt_ctf_field_type(bt_ctf_get_decl_from_def(field)) == CTF_TYPE_ARRAY) {
140 struct definition_array *array_definition;
141 array_definition = container_of(field,
142 struct definition_array, p);
143 ret = bt_array_index(array_definition, index);
144 } else if (bt_ctf_field_type(bt_ctf_get_decl_from_def(field)) == CTF_TYPE_SEQUENCE) {
145 struct definition_sequence *sequence_definition;
146 sequence_definition = container_of(field,
147 struct definition_sequence, p);
148 ret = sequence_index(sequence_definition, index);
149 }
150 return ret;
151 }
152
153 const char *bt_ctf_event_name(const struct bt_ctf_event *ctf_event)
154 {
155 const struct ctf_event_declaration *event_class;
156 const struct ctf_stream_declaration *stream_class;
157 const struct ctf_event_definition *event;
158
159 if (!ctf_event)
160 return NULL;
161
162 event = ctf_event->parent;
163 stream_class = event->stream->stream_class;
164 event_class = g_ptr_array_index(stream_class->events_by_id,
165 event->stream->event_id);
166 return g_quark_to_string(event_class->name);
167 }
168
169 const char *bt_ctf_field_name(const struct definition *def)
170 {
171 if (!def)
172 return NULL;
173
174 return rem_(g_quark_to_string(def->name));
175 }
176
177 enum ctf_type_id bt_ctf_field_type(const struct declaration *decl)
178 {
179 if (!decl)
180 return CTF_TYPE_UNKNOWN;
181
182 return decl->id;
183 }
184
185 int bt_ctf_get_field_list(const struct bt_ctf_event *ctf_event,
186 const struct definition *scope,
187 struct definition const * const **list,
188 unsigned int *count)
189 {
190 if (!ctf_event || !scope || !list || !count)
191 return -EINVAL;
192
193 switch (bt_ctf_field_type(bt_ctf_get_decl_from_def(scope))) {
194 case CTF_TYPE_INTEGER:
195 case CTF_TYPE_FLOAT:
196 case CTF_TYPE_STRING:
197 case CTF_TYPE_ENUM:
198 goto error;
199 case CTF_TYPE_STRUCT:
200 {
201 const struct definition_struct *def_struct;
202
203 def_struct = container_of(scope, const struct definition_struct, p);
204 if (!def_struct)
205 goto error;
206 if (def_struct->fields->pdata) {
207 *list = (struct definition const* const*) def_struct->fields->pdata;
208 *count = def_struct->fields->len;
209 goto end;
210 } else {
211 goto error;
212 }
213 break;
214 }
215 case CTF_TYPE_UNTAGGED_VARIANT:
216 goto error;
217 case CTF_TYPE_VARIANT:
218 {
219 const struct definition_variant *def_variant;
220
221 def_variant = container_of(scope, const struct definition_variant, p);
222 if (!def_variant)
223 goto error;
224 if (def_variant->fields->pdata) {
225 *list = (struct definition const* const*) def_variant->fields->pdata;
226 *count = def_variant->fields->len;
227 goto end;
228 } else {
229 goto error;
230 }
231 break;
232 }
233 case CTF_TYPE_ARRAY:
234 {
235 const struct definition_array *def_array;
236
237 def_array = container_of(scope, const struct definition_array, p);
238 if (!def_array)
239 goto error;
240 if (def_array->elems->pdata) {
241 *list = (struct definition const* const*) def_array->elems->pdata;
242 *count = def_array->elems->len;
243 goto end;
244 } else {
245 goto error;
246 }
247 break;
248 }
249 case CTF_TYPE_SEQUENCE:
250 {
251 const struct definition_sequence *def_sequence;
252
253 def_sequence = container_of(scope, const struct definition_sequence, p);
254 if (!def_sequence)
255 goto error;
256 if (def_sequence->elems->pdata) {
257 *list = (struct definition const* const*) def_sequence->elems->pdata;
258 *count = def_sequence->elems->len;
259 goto end;
260 } else {
261 goto error;
262 }
263 break;
264 }
265 default:
266 break;
267 }
268
269 end:
270 return 0;
271
272 error:
273 *list = NULL;
274 *count = 0;
275 return -1;
276 }
277
278 struct bt_context *bt_ctf_event_get_context(const struct bt_ctf_event *ctf_event)
279 {
280 struct bt_context *ret = NULL;
281 const struct ctf_file_stream *cfs;
282 const struct ctf_trace *trace;
283 const struct ctf_event_definition *event;
284
285 if (!ctf_event)
286 return NULL;
287
288 event = ctf_event->parent;
289 cfs = container_of(event->stream, const struct ctf_file_stream,
290 parent);
291 trace = cfs->parent.stream_class->trace;
292 if (trace->ctx)
293 ret = trace->ctx;
294
295 return ret;
296 }
297
298 int bt_ctf_event_get_handle_id(const struct bt_ctf_event *ctf_event)
299 {
300 int ret = -1;
301 const struct ctf_file_stream *cfs;
302 const struct ctf_trace *trace;
303 const struct ctf_event_definition *event;
304
305 if (!ctf_event)
306 return -EINVAL;
307
308 event = ctf_event->parent;
309 cfs = container_of(event->stream, const struct ctf_file_stream,
310 parent);
311 trace = cfs->parent.stream_class->trace;
312 if (trace->handle)
313 ret = trace->handle->id;
314
315 return ret;
316 }
317
318 uint64_t bt_ctf_get_timestamp(const struct bt_ctf_event *ctf_event)
319 {
320 const struct ctf_event_definition *event;
321
322 if (!ctf_event)
323 return -1ULL;
324
325 event = ctf_event->parent;
326 if (event && event->stream->has_timestamp)
327 return event->stream->real_timestamp;
328 else
329 return -1ULL;
330 }
331
332 uint64_t bt_ctf_get_cycles(const struct bt_ctf_event *ctf_event)
333 {
334 const struct ctf_event_definition *event;
335
336 if (!ctf_event)
337 return -1ULL;
338
339 event = ctf_event->parent;
340 if (event && event->stream->has_timestamp)
341 return event->stream->cycles_timestamp;
342 else
343 return -1ULL;
344 }
345
346 static void bt_ctf_field_set_error(int error)
347 {
348 bt_ctf_last_field_error = error;
349 }
350
351 int bt_ctf_field_get_error(void)
352 {
353 int ret;
354 ret = bt_ctf_last_field_error;
355 bt_ctf_last_field_error = 0;
356
357 return ret;
358 }
359
360 static const struct declaration_integer *
361 get_declaration_integer(const struct declaration *decl)
362 {
363 if (!decl || bt_ctf_field_type(decl) != CTF_TYPE_INTEGER)
364 return NULL;
365 return container_of(decl, const struct declaration_integer, p);
366 }
367
368 static const struct declaration_string *
369 get_declaration_string(const struct declaration *decl)
370 {
371 if (!decl || bt_ctf_field_type(decl) != CTF_TYPE_STRING)
372 return NULL;
373 return container_of(decl, const struct declaration_string, p);
374 }
375
376 static const struct declaration_array *
377 get_declaration_array(const struct declaration *decl)
378 {
379 if (!decl || bt_ctf_field_type(decl) != CTF_TYPE_ARRAY)
380 return NULL;
381 return container_of(decl, const struct declaration_array, p);
382 }
383
384 static const struct declaration_sequence *
385 get_declaration_sequence(const struct declaration *decl)
386 {
387 if (!decl || bt_ctf_field_type(decl) != CTF_TYPE_SEQUENCE)
388 return NULL;
389 return container_of(decl, const struct declaration_sequence, p);
390 }
391
392 int bt_ctf_get_int_signedness(const struct declaration *decl)
393 {
394 const struct declaration_integer *integer;
395
396 integer = get_declaration_integer(decl);
397 if (!integer) {
398 bt_ctf_field_set_error(-EINVAL);
399 return -EINVAL;
400 }
401 return integer->signedness;
402 }
403
404 int bt_ctf_get_int_base(const struct declaration *decl)
405 {
406 const struct declaration_integer *integer;
407
408 integer = get_declaration_integer(decl);
409 if (!integer) {
410 bt_ctf_field_set_error(-EINVAL);
411 return -EINVAL;
412 }
413 return integer->base;
414 }
415
416 int bt_ctf_get_int_byte_order(const struct declaration *decl)
417 {
418 const struct declaration_integer *integer;
419
420 integer = get_declaration_integer(decl);
421 if (!integer) {
422 bt_ctf_field_set_error(-EINVAL);
423 return -EINVAL;
424 }
425 return integer->byte_order;
426 }
427
428 ssize_t bt_ctf_get_int_len(const struct declaration *decl)
429 {
430 const struct declaration_integer *integer;
431
432 integer = get_declaration_integer(decl);
433 if (!integer) {
434 bt_ctf_field_set_error(-EINVAL);
435 return -EINVAL;
436 }
437 return (ssize_t) integer->len;
438 }
439
440 const struct definition *bt_ctf_get_enum_int(const struct definition *field)
441 {
442 const struct definition_enum *def_enum;
443
444 if (!field || bt_ctf_field_type(bt_ctf_get_decl_from_def(field)) != CTF_TYPE_ENUM) {
445 bt_ctf_field_set_error(-EINVAL);
446 return NULL;
447 }
448 def_enum = container_of(field, const struct definition_enum, p);
449 return &def_enum->integer->p;
450 }
451
452 const char *bt_ctf_get_enum_str(const struct definition *field)
453 {
454 const struct definition_enum *def_enum;
455 const struct declaration_enum *decl_enum;
456 GArray *array;
457 const char *ret;
458
459 if (!field || bt_ctf_field_type(bt_ctf_get_decl_from_def(field)) != CTF_TYPE_ENUM) {
460 bt_ctf_field_set_error(-EINVAL);
461 return NULL;
462 }
463 def_enum = container_of(field, const struct definition_enum, p);
464 decl_enum = def_enum->declaration;
465 if (bt_get_int_signedness(&def_enum->integer->p)) {
466 array = bt_enum_int_to_quark_set(decl_enum,
467 bt_get_signed_int(&def_enum->integer->p));
468 } else {
469 array = bt_enum_uint_to_quark_set(decl_enum,
470 bt_get_unsigned_int(&def_enum->integer->p));
471 }
472 if (!array) {
473 bt_ctf_field_set_error(-ENOENT);
474 return NULL;
475 }
476
477 if (array->len == 0) {
478 g_array_unref(array);
479 bt_ctf_field_set_error(-ENOENT);
480 return NULL;
481 }
482 /* Return first string. Arbitrary choice. */
483 ret = g_quark_to_string(g_array_index(array, GQuark, 0));
484 g_array_unref(array);
485 return ret;
486 }
487
488 enum ctf_string_encoding bt_ctf_get_encoding(const struct declaration *decl)
489 {
490 enum ctf_string_encoding ret = 0;
491 enum ctf_type_id type;
492 const struct declaration_integer *integer;
493 const struct declaration_string *string;
494 const struct declaration_array *array;
495 const struct declaration_sequence *sequence;
496
497 if (!decl)
498 goto error;
499
500 type = bt_ctf_field_type(decl);
501
502 switch (type) {
503 case CTF_TYPE_ARRAY:
504 array = get_declaration_array(decl);
505 if (!array)
506 goto error;
507 integer = get_declaration_integer(array->elem);
508 if (!integer)
509 goto error;
510 ret = integer->encoding;
511 break;
512 case CTF_TYPE_SEQUENCE:
513 sequence = get_declaration_sequence(decl);
514 if (!sequence)
515 goto error;
516 integer = get_declaration_integer(sequence->elem);
517 if (!integer)
518 goto error;
519 ret = integer->encoding;
520 break;
521 case CTF_TYPE_STRING:
522 string = get_declaration_string(decl);
523 if (!string)
524 goto error;
525 ret = string->encoding;
526 break;
527 case CTF_TYPE_INTEGER:
528 integer = get_declaration_integer(decl);
529 if (!integer)
530 goto error;
531 ret = integer->encoding;
532 break;
533 default:
534 goto error;
535 }
536 return ret;
537
538 error:
539 bt_ctf_field_set_error(-EINVAL);
540 return -1;
541 }
542
543 int bt_ctf_get_array_len(const struct declaration *decl)
544 {
545 const struct declaration_array *array;
546
547 array = get_declaration_array(decl);
548 if (!array)
549 goto error;
550 return array->len;
551
552 error:
553 bt_ctf_field_set_error(-EINVAL);
554 return -1;
555 }
556
557 uint64_t bt_ctf_get_uint64(const struct definition *field)
558 {
559 uint64_t ret = 0;
560
561 if (field && bt_ctf_field_type(bt_ctf_get_decl_from_def(field)) == CTF_TYPE_INTEGER)
562 ret = bt_get_unsigned_int(field);
563 else
564 bt_ctf_field_set_error(-EINVAL);
565
566 return ret;
567 }
568
569 int64_t bt_ctf_get_int64(const struct definition *field)
570 {
571 int64_t ret = 0;
572
573 if (field && bt_ctf_field_type(bt_ctf_get_decl_from_def(field)) == CTF_TYPE_INTEGER)
574 ret = bt_get_signed_int(field);
575 else
576 bt_ctf_field_set_error(-EINVAL);
577
578 return ret;
579 }
580
581 char *bt_ctf_get_char_array(const struct definition *field)
582 {
583 char *ret = NULL;
584 GString *char_array;
585
586 if (field && bt_ctf_field_type(bt_ctf_get_decl_from_def(field)) == CTF_TYPE_ARRAY) {
587 char_array = bt_get_char_array(field);
588 if (char_array) {
589 ret = char_array->str;
590 goto end;
591 }
592 }
593 bt_ctf_field_set_error(-EINVAL);
594
595 end:
596 return ret;
597 }
598
599 char *bt_ctf_get_string(const struct definition *field)
600 {
601 char *ret = NULL;
602
603 if (field && bt_ctf_field_type(bt_ctf_get_decl_from_def(field)) == CTF_TYPE_STRING)
604 ret = get_string(field);
605 else
606 bt_ctf_field_set_error(-EINVAL);
607
608 return ret;
609 }
610
611 int bt_ctf_get_event_decl_list(int handle_id, struct bt_context *ctx,
612 struct bt_ctf_event_decl * const **list,
613 unsigned int *count)
614 {
615 struct bt_trace_handle *handle;
616 struct trace_descriptor *td;
617 struct ctf_trace *tin;
618
619 if (!ctx || !list || !count)
620 goto error;
621
622 handle = g_hash_table_lookup(ctx->trace_handles,
623 (gpointer) (unsigned long) handle_id);
624 if (!handle)
625 goto error;
626
627 td = handle->td;
628 tin = container_of(td, struct ctf_trace, parent);
629
630 *list = (struct bt_ctf_event_decl * const*) tin->event_declarations->pdata;
631 *count = tin->event_declarations->len;
632 return 0;
633
634 error:
635 return -1;
636 }
637
638 const char *bt_ctf_get_decl_event_name(const struct bt_ctf_event_decl *event)
639 {
640 if (!event)
641 return NULL;
642
643 return g_quark_to_string(event->parent.name);
644 }
645
646 int bt_ctf_get_decl_fields(struct bt_ctf_event_decl *event_decl,
647 enum bt_ctf_scope scope,
648 struct bt_ctf_field_decl const * const **list,
649 unsigned int *count)
650 {
651 int i;
652 GArray *fields = NULL;
653 gpointer *ret_list = NULL;
654 GPtrArray *fields_array = NULL;
655 int ret = 0;
656 *count = 0;
657
658 if (!event_decl || !list || !count)
659 return -EINVAL;
660
661 switch (scope) {
662 case BT_EVENT_CONTEXT:
663 if (event_decl->context_decl) {
664 ret_list = event_decl->context_decl->pdata;
665 *count = event_decl->context_decl->len;
666 goto end;
667 }
668 event_decl->context_decl = g_ptr_array_new();
669 if (!event_decl->parent.context_decl) {
670 ret = -1;
671 goto end;
672 }
673 fields = event_decl->parent.context_decl->fields;
674 fields_array = event_decl->context_decl;
675 break;
676 case BT_EVENT_FIELDS:
677 if (event_decl->fields_decl) {
678 ret_list = event_decl->fields_decl->pdata;
679 *count = event_decl->fields_decl->len;
680 goto end;
681 }
682 event_decl->fields_decl = g_ptr_array_new();
683 if (!event_decl->parent.fields_decl) {
684 ret = -1;
685 goto end;
686 }
687 fields = event_decl->parent.fields_decl->fields;
688 fields_array = event_decl->fields_decl;
689 break;
690 case BT_STREAM_PACKET_CONTEXT:
691 if (event_decl->packet_context_decl) {
692 ret_list = event_decl->packet_context_decl->pdata;
693 *count = event_decl->packet_context_decl->len;
694 goto end;
695 }
696 event_decl->packet_context_decl = g_ptr_array_new();
697 if (!event_decl->parent.stream->packet_context_decl) {
698 ret = -1;
699 goto end;
700 }
701 fields = event_decl->parent.stream->packet_context_decl->fields;
702 fields_array = event_decl->packet_context_decl;
703 break;
704 case BT_STREAM_EVENT_CONTEXT:
705 if (event_decl->event_context_decl) {
706 ret_list = event_decl->event_context_decl->pdata;
707 *count = event_decl->event_context_decl->len;
708 goto end;
709 }
710 event_decl->event_context_decl = g_ptr_array_new();
711 if (!event_decl->parent.stream->event_context_decl) {
712 ret = -1;
713 goto end;
714 }
715 fields = event_decl->parent.stream->event_context_decl->fields;
716 fields_array = event_decl->event_context_decl;
717 break;
718 case BT_STREAM_EVENT_HEADER:
719 if (event_decl->event_header_decl) {
720 ret_list = event_decl->event_header_decl->pdata;
721 *count = event_decl->event_header_decl->len;
722 goto end;
723 }
724 event_decl->event_header_decl = g_ptr_array_new();
725 if (!event_decl->parent.stream->event_header_decl) {
726 ret = -1;
727 goto end;
728 }
729 fields = event_decl->parent.stream->event_header_decl->fields;
730 fields_array = event_decl->event_header_decl;
731 break;
732 case BT_TRACE_PACKET_HEADER:
733 if (event_decl->packet_header_decl) {
734 ret_list = event_decl->packet_header_decl->pdata;
735 *count = event_decl->packet_header_decl->len;
736 goto end;
737 }
738 event_decl->packet_header_decl = g_ptr_array_new();
739 if (!event_decl->parent.stream->trace->packet_header_decl) {
740 ret = -1;
741 goto end;
742 }
743 fields = event_decl->parent.stream->trace->packet_header_decl->fields;
744 fields_array = event_decl->packet_header_decl;
745 break;
746 }
747
748 for (i = 0; i < fields->len; i++) {
749 g_ptr_array_add(fields_array,
750 &g_array_index(fields,
751 struct declaration_field, i));
752 }
753 ret_list = fields_array->pdata;
754 *count = fields->len;
755
756 end:
757 *list = (struct bt_ctf_field_decl const* const*) ret_list;
758
759 return ret;
760 }
761
762 const char *bt_ctf_get_decl_field_name(const struct bt_ctf_field_decl *field)
763 {
764 if (!field)
765 return NULL;
766
767 return rem_(g_quark_to_string(((struct declaration_field *) field)->name));
768 }
769
770 const struct declaration *bt_ctf_get_decl_from_def(const struct definition *def)
771 {
772 if (def)
773 return def->declaration;
774
775 return NULL;
776 }
777
778 const struct declaration *bt_ctf_get_decl_from_field_decl(
779 const struct bt_ctf_field_decl *field)
780 {
781 if (field)
782 return ((struct declaration_field *) field)->declaration;
783
784 return NULL;
785 }
This page took 0.04434 seconds and 4 git commands to generate.