quit menu complete
[lttv.git] / ltt / branches / poly / lttv / processTrace.c
CommitLineData
dc877563 1
2#include <lttv/processTrace.h>
ffd54a90 3#include <ltt/event.h>
b445142a 4#include <ltt/facility.h>
dc877563 5
ffd54a90 6void lttv_context_init(LttvTracesetContext *self, LttvTraceset *ts)
dc877563 7{
ffd54a90 8 LTTV_TRACESET_CONTEXT_GET_CLASS(self)->init(self, ts);
dc877563 9}
10
11
12void lttv_context_fini(LttvTracesetContext *self)
13{
14 LTTV_TRACESET_CONTEXT_GET_CLASS(self)->fini(self);
15}
16
17
18LttvTracesetContext *
19lttv_context_new_traceset_context(LttvTracesetContext *self)
20{
21 return LTTV_TRACESET_CONTEXT_GET_CLASS(self)->new_traceset_context(self);
22}
23
24
25
26
27LttvTraceContext *
28lttv_context_new_trace_context(LttvTracesetContext *self)
29{
30 return LTTV_TRACESET_CONTEXT_GET_CLASS(self)->new_trace_context(self);
31}
32
33
34LttvTracefileContext *
35lttv_context_new_tracefile_context(LttvTracesetContext *self)
36{
c6bc9cb9 37 return LTTV_TRACESET_CONTEXT_GET_CLASS(self)->new_tracefile_context(self);
dc877563 38}
39
40
41static void
42init(LttvTracesetContext *self, LttvTraceset *ts)
43{
44 guint i, j, nb_trace, nb_control, nb_per_cpu, nb_tracefile;
45
46 LttvTraceContext *tc;
47
48 LttvTracefileContext *tfc;
49
50 nb_trace = lttv_traceset_number(ts);
51 self->ts = ts;
ffd54a90 52 self->traces = g_new(LttvTraceContext *, nb_trace);
dc877563 53 self->before = lttv_hooks_new();
54 self->after = lttv_hooks_new();
ffd54a90 55 self->a = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL);
dc877563 56 for(i = 0 ; i < nb_trace ; i++) {
ffd54a90 57 tc = LTTV_TRACESET_CONTEXT_GET_CLASS(self)->new_trace_context(self);
dc877563 58 self->traces[i] = tc;
59
60 tc->ts_context = self;
61 tc->index = i;
62 tc->t = lttv_traceset_get(ts, i);
63 tc->check = lttv_hooks_new();
64 tc->before = lttv_hooks_new();
65 tc->after = lttv_hooks_new();
ffd54a90 66 tc->a = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL);
dc877563 67 nb_control = ltt_trace_control_tracefile_number(tc->t);
68 nb_per_cpu = ltt_trace_per_cpu_tracefile_number(tc->t);
69 nb_tracefile = nb_control + nb_per_cpu;
ffd54a90 70 tc->control_tracefiles = g_new(LttvTracefileContext *, nb_control);
71 tc->per_cpu_tracefiles = g_new(LttvTracefileContext *, nb_per_cpu);
dc877563 72
73 for(j = 0 ; j < nb_tracefile ; j++) {
ffd54a90 74 tfc = LTTV_TRACESET_CONTEXT_GET_CLASS(self)->new_tracefile_context(self);
dc877563 75 if(j < nb_control) {
76 tc->control_tracefiles[j] = tfc;
77 tfc->control = TRUE;
78 tfc->index = j;
ffd54a90 79 tfc->tf = ltt_trace_control_tracefile_get(tc->t, j);
dc877563 80 }
81 else {
82 tc->per_cpu_tracefiles[j - nb_control] = tfc;
83 tfc->control = FALSE;
84 tfc->index = j - nb_control;
ffd54a90 85 tfc->tf = ltt_trace_per_cpu_tracefile_get(tc->t, j - nb_control);
dc877563 86 }
87 tfc->t_context = tc;
88 tfc->check = lttv_hooks_new();
89 tfc->before = lttv_hooks_new();
90 tfc->after = lttv_hooks_new();
91 tfc->check_event = lttv_hooks_new();
92 tfc->before_event = lttv_hooks_new();
93 tfc->before_event_by_id = lttv_hooks_by_id_new();
94 tfc->after_event = lttv_hooks_new();
95 tfc->after_event_by_id = lttv_hooks_by_id_new();
ffd54a90 96 tfc->a = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL);
dc877563 97 }
98 }
99}
100
101
102void fini(LttvTracesetContext *self)
103{
104 guint i, j, nb_trace, nb_control, nb_per_cpu, nb_tracefile;
105
106 LttvTraceContext *tc;
107
108 LttvTracefileContext *tfc;
109
ffd54a90 110 LttvTraceset *ts = self->ts;
dc877563 111
112 lttv_hooks_destroy(self->before);
113 lttv_hooks_destroy(self->after);
ffd54a90 114 g_object_unref(self->a);
dc877563 115
116 nb_trace = lttv_traceset_number(ts);
117
118 for(i = 0 ; i < nb_trace ; i++) {
119 tc = self->traces[i];
120
121 lttv_hooks_destroy(tc->check);
122 lttv_hooks_destroy(tc->before);
123 lttv_hooks_destroy(tc->after);
ffd54a90 124 g_object_unref(tc->a);
dc877563 125
126 nb_control = ltt_trace_control_tracefile_number(tc->t);
127 nb_per_cpu = ltt_trace_per_cpu_tracefile_number(tc->t);
128 nb_tracefile = nb_control + nb_per_cpu;
129
130 for(j = 0 ; j < nb_tracefile ; j++) {
131 if(j < nb_control) tfc = tc->control_tracefiles[j];
132 else tfc = tc->per_cpu_tracefiles[j - nb_control];
133
134 lttv_hooks_destroy(tfc->check);
135 lttv_hooks_destroy(tfc->before);
136 lttv_hooks_destroy(tfc->after);
137 lttv_hooks_destroy(tfc->check_event);
138 lttv_hooks_destroy(tfc->before_event);
139 lttv_hooks_by_id_destroy(tfc->before_event_by_id);
140 lttv_hooks_destroy(tfc->after_event);
141 lttv_hooks_by_id_destroy(tfc->after_event_by_id);
ffd54a90 142 g_object_unref(tfc->a);
dc877563 143 g_object_unref(tfc);
144 }
145 g_free(tc->control_tracefiles);
146 g_free(tc->per_cpu_tracefiles);
147 g_object_unref(tc);
148 }
149 g_free(self->traces);
150}
151
152
153void lttv_traceset_context_add_hooks(LttvTracesetContext *self,
154 LttvHooks *before_traceset,
155 LttvHooks *after_traceset,
156 LttvHooks *check_trace,
157 LttvHooks *before_trace,
158 LttvHooks *after_trace,
ffd54a90 159 LttvHooks *check_tracefile,
160 LttvHooks *before_tracefile,
161 LttvHooks *after_tracefile,
dc877563 162 LttvHooks *check_event,
163 LttvHooks *before_event,
164 LttvHooks *after_event)
165{
166 LttvTraceset *ts = self->ts;
167
168 guint i, j, nb_trace, nb_control, nb_per_cpu, nb_tracefile;
169
170 LttvTraceContext *tc;
171
172 LttvTracefileContext *tfc;
173
174 void *hook_data;
175
176 lttv_hooks_add_list(self->before, before_traceset);
177 lttv_hooks_add_list(self->after, after_traceset);
178 nb_trace = lttv_traceset_number(ts);
179
180 for(i = 0 ; i < nb_trace ; i++) {
181 tc = self->traces[i];
182 lttv_hooks_add_list(tc->check, check_trace);
183 lttv_hooks_add_list(tc->before, before_trace);
184 lttv_hooks_add_list(tc->after, after_trace);
185 nb_control = ltt_trace_control_tracefile_number(tc->t);
d83f6739 186 nb_per_cpu = ltt_trace_per_cpu_tracefile_number(tc->t);
dc877563 187 nb_tracefile = nb_control + nb_per_cpu;
188
189 for(j = 0 ; j < nb_tracefile ; j++) {
190 if(j < nb_control) {
191 tfc = tc->control_tracefiles[j];
192 }
193 else {
d83f6739 194 tfc = tc->per_cpu_tracefiles[j-nb_control];
dc877563 195 }
196 lttv_hooks_add_list(tfc->check, check_tracefile);
197 lttv_hooks_add_list(tfc->before, before_tracefile);
198 lttv_hooks_add_list(tfc->after, after_tracefile);
199 lttv_hooks_add_list(tfc->check_event, check_event);
200 lttv_hooks_add_list(tfc->before_event, before_event);
201 lttv_hooks_add_list(tfc->after_event, after_event);
202 }
203 }
204}
205
206
207void lttv_traceset_context_remove_hooks(LttvTracesetContext *self,
208 LttvHooks *before_traceset,
209 LttvHooks *after_traceset,
210 LttvHooks *check_trace,
211 LttvHooks *before_trace,
212 LttvHooks *after_trace,
ffd54a90 213 LttvHooks *check_tracefile,
214 LttvHooks *before_tracefile,
215 LttvHooks *after_tracefile,
dc877563 216 LttvHooks *check_event,
217 LttvHooks *before_event,
218 LttvHooks *after_event)
219{
220 LttvTraceset *ts = self->ts;
221
222 guint i, j, nb_trace, nb_control, nb_per_cpu, nb_tracefile;
223
224 LttvTraceContext *tc;
225
226 LttvTracefileContext *tfc;
227
228 void *hook_data;
229
230 lttv_hooks_remove_list(self->before, before_traceset);
231 lttv_hooks_remove_list(self->after, after_traceset);
232 nb_trace = lttv_traceset_number(ts);
233
234 for(i = 0 ; i < nb_trace ; i++) {
235 tc = self->traces[i];
236 lttv_hooks_remove_list(tc->check, check_trace);
237 lttv_hooks_remove_list(tc->before, before_trace);
238 lttv_hooks_remove_list(tc->after, after_trace);
239 nb_control = ltt_trace_control_tracefile_number(tc->t);
d83f6739 240 nb_per_cpu = ltt_trace_per_cpu_tracefile_number(tc->t);
dc877563 241 nb_tracefile = nb_control + nb_per_cpu;
242
243 for(j = 0 ; j < nb_tracefile ; j++) {
244 if(j < nb_control) {
245 tfc = tc->control_tracefiles[j];
246 }
247 else {
d83f6739 248 tfc = tc->per_cpu_tracefiles[j-nb_control];
dc877563 249 }
250 lttv_hooks_remove_list(tfc->check, check_tracefile);
251 lttv_hooks_remove_list(tfc->before, before_tracefile);
252 lttv_hooks_remove_list(tfc->after, after_tracefile);
253 lttv_hooks_remove_list(tfc->check_event, check_event);
254 lttv_hooks_remove_list(tfc->before_event, before_event);
255 lttv_hooks_remove_list(tfc->after_event, after_event);
256 }
257 }
258}
259
260
ba576a78 261static LttvTracesetContext *
dc877563 262new_traceset_context(LttvTracesetContext *self)
263{
ffd54a90 264 return g_object_new(LTTV_TRACESET_CONTEXT_TYPE, NULL);
dc877563 265}
266
267
ba576a78 268static LttvTraceContext *
dc877563 269new_trace_context(LttvTracesetContext *self)
270{
ffd54a90 271 return g_object_new(LTTV_TRACE_CONTEXT_TYPE, NULL);
dc877563 272}
273
274
ba576a78 275static LttvTracefileContext *
dc877563 276new_tracefile_context(LttvTracesetContext *self)
277{
ffd54a90 278 return g_object_new(LTTV_TRACEFILE_CONTEXT_TYPE, NULL);
dc877563 279}
280
281
282static void
283traceset_context_instance_init (GTypeInstance *instance, gpointer g_class)
284{
285 /* Be careful of anything which would not work well with shallow copies */
286}
287
288
289static void
290traceset_context_finalize (LttvTracesetContext *self)
291{
b445142a 292 G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACESET_CONTEXT_TYPE)))
293 ->finalize(G_OBJECT(self));
dc877563 294}
295
296
297static void
298traceset_context_class_init (LttvTracesetContextClass *klass)
299{
300 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
301
ffd54a90 302 gobject_class->finalize = (void (*)(GObject *self))traceset_context_finalize;
dc877563 303 klass->init = init;
304 klass->fini = fini;
305 klass->new_traceset_context = new_traceset_context;
306 klass->new_trace_context = new_trace_context;
307 klass->new_tracefile_context = new_tracefile_context;
308}
309
310
311GType
ffd54a90 312lttv_traceset_context_get_type(void)
dc877563 313{
314 static GType type = 0;
315 if (type == 0) {
316 static const GTypeInfo info = {
ffd54a90 317 sizeof (LttvTracesetContextClass),
dc877563 318 NULL, /* base_init */
319 NULL, /* base_finalize */
ffd54a90 320 (GClassInitFunc) traceset_context_class_init, /* class_init */
dc877563 321 NULL, /* class_finalize */
322 NULL, /* class_data */
ffd54a90 323 sizeof (LttvTracesetContext),
dc877563 324 0, /* n_preallocs */
ffd54a90 325 (GInstanceInitFunc) traceset_context_instance_init /* instance_init */
dc877563 326 };
327
ffd54a90 328 type = g_type_register_static (G_TYPE_OBJECT, "LttvTracesetContextType",
dc877563 329 &info, 0);
330 }
331 return type;
332}
333
334
335static void
336trace_context_instance_init (GTypeInstance *instance, gpointer g_class)
337{
338 /* Be careful of anything which would not work well with shallow copies */
339}
340
341
342static void
343trace_context_finalize (LttvTraceContext *self)
344{
b445142a 345 G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACE_CONTEXT_TYPE)))->
346 finalize(G_OBJECT(self));
dc877563 347}
348
349
350static void
351trace_context_class_init (LttvTraceContextClass *klass)
352{
353 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
354
ffd54a90 355 gobject_class->finalize = (void (*)(GObject *self)) trace_context_finalize;
dc877563 356}
357
358
359GType
ffd54a90 360lttv_trace_context_get_type(void)
dc877563 361{
362 static GType type = 0;
363 if (type == 0) {
364 static const GTypeInfo info = {
ffd54a90 365 sizeof (LttvTraceContextClass),
dc877563 366 NULL, /* base_init */
367 NULL, /* base_finalize */
ffd54a90 368 (GClassInitFunc) trace_context_class_init, /* class_init */
dc877563 369 NULL, /* class_finalize */
370 NULL, /* class_data */
c6bc9cb9 371 sizeof (LttvTraceContext),
dc877563 372 0, /* n_preallocs */
ffd54a90 373 (GInstanceInitFunc) trace_context_instance_init /* instance_init */
dc877563 374 };
375
ffd54a90 376 type = g_type_register_static (G_TYPE_OBJECT, "LttvTraceContextType",
dc877563 377 &info, 0);
378 }
379 return type;
380}
381
382
383static void
384tracefile_context_instance_init (GTypeInstance *instance, gpointer g_class)
385{
386 /* Be careful of anything which would not work well with shallow copies */
387}
388
389
390static void
391tracefile_context_finalize (LttvTracefileContext *self)
392{
b445142a 393 G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACEFILE_CONTEXT_TYPE)))
394 ->finalize(G_OBJECT(self));
dc877563 395}
396
397
398static void
399tracefile_context_class_init (LttvTracefileContextClass *klass)
400{
401 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
402
ffd54a90 403 gobject_class->finalize = (void (*)(GObject *self))tracefile_context_finalize;
404}
405
406
407GType
408lttv_tracefile_context_get_type(void)
409{
410 static GType type = 0;
411 if (type == 0) {
412 static const GTypeInfo info = {
413 sizeof (LttvTracefileContextClass),
414 NULL, /* base_init */
415 NULL, /* base_finalize */
416 (GClassInitFunc) tracefile_context_class_init, /* class_init */
417 NULL, /* class_finalize */
418 NULL, /* class_data */
419 sizeof (LttvTracefileContext),
420 0, /* n_preallocs */
421 (GInstanceInitFunc) tracefile_context_instance_init /* instance_init */
422 };
423
424 type = g_type_register_static (G_TYPE_OBJECT, "LttvTracefileContextType",
425 &info, 0);
426 }
427 return type;
dc877563 428}
429
430
ffd54a90 431gint compare_tracefile(gconstpointer a, gconstpointer b)
dc877563 432{
ffd54a90 433 if(((LttvTime *)a)->tv_sec > ((LttvTime *)b)->tv_sec) return 1;
434 if(((LttvTime *)a)->tv_sec < ((LttvTime *)b)->tv_sec) return -1;
435 if(((LttvTime *)a)->tv_nsec > ((LttvTime *)b)->tv_nsec) return 1;
436 if(((LttvTime *)a)->tv_nsec < ((LttvTime *)b)->tv_nsec) return -1;
dc877563 437 return 0;
438}
439
440
441gboolean get_first(gpointer key, gpointer value, gpointer user_data) {
442 *((LttvTracefileContext **)user_data) = (LttvTracefileContext *)value;
443 return TRUE;
444}
445
446
ffd54a90 447void lttv_process_trace(LttTime start, LttTime end, LttvTraceset *traceset,
270e7cc5 448 LttvTracesetContext *context, unsigned maxNumEvents)
dc877563 449{
450 GPtrArray *traces = g_ptr_array_new();
451
452 GPtrArray *tracefiles = g_ptr_array_new();
453
454 GTree *pqueue = g_tree_new(compare_tracefile);
455
ffd54a90 456 guint i, j, nbi, nbj, id, nb_control, nb_cpu;
dc877563 457
458 LttTrace *trace;
459
460 LttvTraceContext *tc;
461
462 LttTracefile *tracefile;
463
464 LttvTracefileContext *tfc;
465
466 LttEvent *event;
270e7cc5 467 unsigned count = 0;
468 LttTime preTimestamp;
dc877563 469
470 /* Call all before_traceset, before_trace, and before_tracefile hooks.
471 For all qualifying tracefiles, seek to the start time, create a context,
472 read one event and insert in the pqueue based on the event time. */
473
474 lttv_hooks_call(context->before, context);
cbe7c836 475 nbi = lttv_traceset_number(traceset);
476 // nbi = ltt_trace_set_number(traceset);
dc877563 477
478 for(i = 0 ; i < nbi ; i++) {
479 tc = context->traces[i];
480 trace = tc->t;
481
482 if(!lttv_hooks_call_check(tc->check, tc)) {
483 g_ptr_array_add(traces, tc);
484 lttv_hooks_call(tc->before, tc);
485 nb_control = ltt_trace_control_tracefile_number(trace);
486 nb_cpu = ltt_trace_per_cpu_tracefile_number(trace);
487 nbj = nb_control + nb_cpu;
488
489 for(j = 0 ; j < nbj ; j++) {
490 if(j < nb_control) {
491 tfc = tc->control_tracefiles[j];
492 }
493 else {
494 tfc = tc->per_cpu_tracefiles[j - nb_control];
495 }
496
497 tracefile = tfc->tf;
498
499 if(!lttv_hooks_call_check(tfc->check, tfc)) {
500 g_ptr_array_add(tracefiles, tfc);
501 lttv_hooks_call(tfc->before, tfc);
502
503 ltt_tracefile_seek_time(tracefile, start);
504 event = ltt_tracefile_read(tracefile);
505 tfc->e = event;
506
507 if(event != NULL) {
ffd54a90 508 tfc->timestamp = ltt_event_time(event);
509 g_tree_insert(pqueue, &(tfc->timestamp), tfc);
dc877563 510 }
511 }
512 }
513 }
514 }
515
516 /* Get the next event from the pqueue, call its hooks,
517 reinsert in the pqueue the following event from the same tracefile
518 unless the tracefile is finished or the event is later than the
519 start time. */
520
ffd54a90 521 while(TRUE) {
dc877563 522 tfc = NULL;
523 g_tree_foreach(pqueue, get_first, &tfc);
524 if(tfc == NULL) break;
525
526 /* Get the tracefile with an event for the smallest time found. If two
527 or more tracefiles have events for the same time, hope that lookup
528 and remove are consistent. */
529
270e7cc5 530 count++;
531 if(count > maxNumEvents){
532 if(tfc->timestamp.tv_sec == preTimestamp.tv_sec &&
533 tfc->timestamp.tv_nsec == preTimestamp.tv_nsec) {
534 count--;
535 }else{
536 while(TRUE){
537 tfc = NULL;
538 g_tree_foreach(pqueue, get_first, &tfc);
539 if(tfc == NULL) break;
540 g_tree_remove(pqueue, &(tfc->timestamp));
541 }
542 break;
543 }
544 }
545 preTimestamp = tfc->timestamp;
546
ffd54a90 547 tfc = g_tree_lookup(pqueue, &(tfc->timestamp));
548 g_tree_remove(pqueue, &(tfc->timestamp));
dc877563 549
b445142a 550 if(!lttv_hooks_call(tfc->check_event, tfc)) {
cbe7c836 551 id = ltt_event_eventtype_id(tfc->e);
dc877563 552 lttv_hooks_call(tfc->before_event, tfc);
553 lttv_hooks_call(lttv_hooks_by_id_get(tfc->before_event_by_id, id), tfc);
b445142a 554 lttv_hooks_call(tfc->after_event, tfc);
dc877563 555 lttv_hooks_call(lttv_hooks_by_id_get(tfc->after_event_by_id, id), tfc);
556 }
557
558 event = ltt_tracefile_read(tfc->tf);
559 if(event != NULL) {
560 tfc->e = event;
ffd54a90 561 tfc->timestamp = ltt_event_time(event);
270e7cc5 562 if(tfc->timestamp.tv_sec < end.tv_sec ||
563 (tfc->timestamp.tv_sec == end.tv_sec && tfc->timestamp.tv_nsec <= end.tv_nsec))
564 g_tree_insert(pqueue, &(tfc->timestamp), tfc);
dc877563 565 }
566 }
567
568 /* Call all the after_tracefile, after_trace and after_traceset hooks. */
569
570 for(i = 0, j = 0 ; i < traces->len ; i++) {
571 tc = traces->pdata[i];
572 while(j < tracefiles->len) {
573 tfc = tracefiles->pdata[j];
574
575 if(tfc->t_context == tc) {
576 lttv_hooks_call(tfc->after, tfc);
577 j++;
578 }
579 else break;
580 }
581 lttv_hooks_call(tc->after, tc);
582 }
583
584 g_assert(j == tracefiles->len);
ffd54a90 585 lttv_hooks_call(context->after, context);
dc877563 586
587 /* Free the traces, tracefiles and pqueue */
588
589 g_ptr_array_free(tracefiles, TRUE);
590 g_ptr_array_free(traces, TRUE);
ffd54a90 591 g_tree_destroy(pqueue);
dc877563 592}
b445142a 593
594static LttField *
595find_field(LttEventType *et, const char *field)
596{
597 LttType *t;
598
599 LttField *f;
600
601 guint i, nb;
602
603 char *name;
604
605 if(field == NULL) return NULL;
606
607 f = ltt_eventtype_field(et);
608 t = ltt_eventtype_type(et);
609 g_assert(ltt_type_class(t) == LTT_STRUCT);
610 nb = ltt_type_member_number(t);
611 for(i = 0 ; i < nb ; i++) {
612 ltt_type_member_type(t, i, &name);
613 if(strcmp(name, field) == 0) break;
614 }
615 g_assert(i < nb);
616 return ltt_field_member(f, i);
617}
618
619
620void
621lttv_trace_find_hook(LttTrace *t, char *facility, char *event_type,
622 char *field1, char *field2, char *field3, LttvHook h, LttvTraceHook *th)
623{
624 LttFacility *f;
625
626 LttEventType *et;
627
628 guint nb, pos, i;
629
630 char *name;
631
632 nb = ltt_trace_facility_find(t, facility, &pos);
633 if(nb < 1) g_error("No %s facility", facility);
634 f = ltt_trace_facility_get(t, pos);
635 et = ltt_facility_eventtype_get_by_name(f, event_type);
636 if(et == NULL) g_error("Event %s does not exist", event_type);
637
638 th->h = h;
639 th->id = ltt_eventtype_id(et);
640 th->f1 = find_field(et, field1);
641 th->f2 = find_field(et, field2);
642 th->f3 = find_field(et, field3);
643}
644
645
This page took 0.049025 seconds and 4 git commands to generate.