pa_microbench.c revision 1.1.1.1 1 #include "test/jemalloc_test.h"
2
3 /* Additional includes for PA functionality */
4 #include "jemalloc/internal/pa.h"
5 #include "jemalloc/internal/tsd.h"
6 #include "jemalloc/internal/sz.h"
7 #include "jemalloc/internal/base.h"
8 #include "jemalloc/internal/ehooks.h"
9 #include "jemalloc/internal/nstime.h"
10 #include "jemalloc/internal/hpa.h"
11 #include "jemalloc/internal/sec.h"
12 #include "jemalloc/internal/emap.h"
13 #include "jemalloc/internal/psset.h"
14
15 /*
16 * PA Microbenchmark (Simplified Version)
17 *
18 * This tool reads allocation traces and simulates PA behavior
19 * for testing and understanding the allocation patterns.
20 *
21 * Features:
22 * 1. Reads CSV input file with format: shard_ind,operation,size_or_alloc_index,is_frequent
23 * 2. Simulates allocations/deallocations tracking
24 * 3. Provides basic statistics analysis
25 * 4. Validates the framework setup
26 */
27
28 #define MAX_LINE_LENGTH 1024
29 #define MAX_ALLOCATIONS 10000000
30 #define MAX_ARENAS 128
31
32 typedef enum { PA_ALLOC = 0, PA_DALLOC = 1 } pa_op_t;
33
34 typedef struct {
35 int shard_ind;
36 pa_op_t operation;
37 size_t size_or_alloc_index;
38 uint64_t nsecs;
39 int is_frequent;
40 } pa_event_t;
41
42 typedef struct {
43 edata_t *edata;
44 size_t size;
45 int shard_ind;
46 bool active;
47 } allocation_record_t;
48
49 /* Structure to group per-shard tracking statistics */
50 typedef struct {
51 uint64_t alloc_count; /* Number of allocations */
52 uint64_t dealloc_count; /* Number of deallocations */
53 uint64_t bytes_allocated; /* Current bytes allocated */
54 } shard_stats_t;
55
56 /* Structure to group per-shard PA infrastructure */
57 typedef struct {
58 base_t *base; /* Base allocator */
59 emap_t emap; /* Extent map */
60 pa_shard_t pa_shard; /* PA shard */
61 pa_shard_stats_t shard_stats; /* PA shard statistics */
62 malloc_mutex_t stats_mtx; /* Statistics mutex */
63 } shard_infrastructure_t;
64
65 static FILE *g_stats_output = NULL; /* Output file for stats */
66 static size_t g_alloc_counter = 0; /* Global allocation counter */
67 static allocation_record_t *g_alloc_records =
68 NULL; /* Global allocation tracking */
69 static bool g_use_sec = true; /* Global flag for SEC vs HPA-only */
70
71 /* Refactored arrays using structures */
72 static shard_stats_t *g_shard_stats = NULL; /* Per-shard tracking statistics */
73 static shard_infrastructure_t *g_shard_infra =
74 NULL; /* Per-shard PA infrastructure */
75 static pa_central_t g_pa_central; /* Global PA central */
76
77 /* Override for curtime */
78 static hpa_hooks_t hpa_hooks_override;
79 static nstime_t cur_time_clock;
80
81 void
82 curtime(nstime_t *r_time, bool first_reading) {
83 if (first_reading) {
84 nstime_init_zero(r_time);
85 }
86 *r_time = cur_time_clock;
87 }
88
89 static void
90 set_clock(uint64_t nsecs) {
91 nstime_init(&cur_time_clock, nsecs);
92 }
93
94 static void
95 init_hpa_hooks() {
96 hpa_hooks_override = hpa_hooks_default;
97 hpa_hooks_override.curtime = curtime;
98 }
99
100 static void cleanup_pa_infrastructure(int num_shards);
101
102 static bool
103 initialize_pa_infrastructure(int num_shards) {
104 /*
105 * Note when we call malloc, it resolves to je_malloc, while internal
106 * functions like base_new resolve to jet_malloc. This is because this
107 * file is compiled with -DJEMALLOC_JET as a test. This allows us to
108 * completely isolate the PA infrastructure benchmark from the rest of
109 * the jemalloc usage.
110 */
111 void *dummy_jet = jet_malloc(16);
112 if (dummy_jet == NULL) {
113 fprintf(stderr, "Failed to initialize JET jemalloc\n");
114 return 1;
115 }
116
117 /* Force JET system to be fully initialized */
118 if (jet_mallctl("epoch", NULL, NULL, NULL, 0) != 0) {
119 fprintf(stderr, "Failed to initialize JET system fully\n");
120 jet_free(dummy_jet);
121 return 1;
122 }
123 jet_free(dummy_jet);
124
125 /* Allocate shard tracking statistics */
126 g_shard_stats = calloc(num_shards, sizeof(shard_stats_t));
127 if (g_shard_stats == NULL) {
128 printf("DEBUG: Failed to allocate shard stats\n");
129 return true;
130 }
131
132 /* Allocate shard infrastructure */
133 g_shard_infra = calloc(num_shards, sizeof(shard_infrastructure_t));
134 if (g_shard_infra == NULL) {
135 printf("DEBUG: Failed to allocate shard infrastructure\n");
136 free(g_shard_stats);
137 return true;
138 }
139
140 /* Initialize one base allocator for PA central */
141 base_t *central_base = base_new(tsd_tsdn(tsd_fetch()), 0 /* ind */,
142 (extent_hooks_t *)&ehooks_default_extent_hooks,
143 /* metadata_use_hooks */ true);
144 if (central_base == NULL) {
145 printf("DEBUG: Failed to create central_base\n");
146 free(g_shard_stats);
147 free(g_shard_infra);
148 return true;
149 }
150
151 /* Initialize PA central with HPA enabled */
152 init_hpa_hooks();
153 if (pa_central_init(&g_pa_central, central_base, true /* hpa */,
154 &hpa_hooks_override)) {
155 printf("DEBUG: Failed to initialize PA central\n");
156 base_delete(tsd_tsdn(tsd_fetch()), central_base);
157 free(g_shard_stats);
158 free(g_shard_infra);
159 return true;
160 }
161
162 for (int i = 0; i < num_shards; i++) {
163 /* Create a separate base allocator for each shard */
164 g_shard_infra[i].base = base_new(tsd_tsdn(tsd_fetch()),
165 i /* ind */, (extent_hooks_t *)&ehooks_default_extent_hooks,
166 /* metadata_use_hooks */ true);
167 if (g_shard_infra[i].base == NULL) {
168 printf("DEBUG: Failed to create base %d\n", i);
169 /* Clean up partially initialized shards */
170 cleanup_pa_infrastructure(num_shards);
171 return true;
172 }
173
174 /* Initialize emap for this shard */
175 if (emap_init(&g_shard_infra[i].emap, g_shard_infra[i].base,
176 /* zeroed */ false)) {
177 printf("DEBUG: Failed to initialize emap %d\n", i);
178 /* Clean up partially initialized shards */
179 cleanup_pa_infrastructure(num_shards);
180 return true;
181 }
182
183 /* Initialize stats mutex */
184 if (malloc_mutex_init(&g_shard_infra[i].stats_mtx,
185 "pa_shard_stats", WITNESS_RANK_OMIT,
186 malloc_mutex_rank_exclusive)) {
187 printf(
188 "DEBUG: Failed to initialize stats mutex %d\n", i);
189 /* Clean up partially initialized shards */
190 cleanup_pa_infrastructure(num_shards);
191 return true;
192 }
193
194 /* Initialize PA shard */
195 nstime_t cur_time;
196 nstime_init_zero(&cur_time);
197
198 if (pa_shard_init(tsd_tsdn(tsd_fetch()),
199 &g_shard_infra[i].pa_shard, &g_pa_central,
200 &g_shard_infra[i].emap /* emap */,
201 g_shard_infra[i].base, i /* ind */,
202 &g_shard_infra[i].shard_stats /* stats */,
203 &g_shard_infra[i].stats_mtx /* stats_mtx */,
204 &cur_time /* cur_time */,
205 SIZE_MAX /* oversize_threshold */,
206 -1 /* dirty_decay_ms */, -1 /* muzzy_decay_ms */)) {
207 printf("DEBUG: Failed to initialize PA shard %d\n", i);
208 /* Clean up partially initialized shards */
209 cleanup_pa_infrastructure(num_shards);
210 return true;
211 }
212
213 /* Enable HPA for this shard with proper configuration */
214 hpa_shard_opts_t hpa_opts = HPA_SHARD_OPTS_DEFAULT;
215 hpa_opts.deferral_allowed =
216 false; /* No background threads in microbench */
217
218 sec_opts_t sec_opts = SEC_OPTS_DEFAULT;
219 if (!g_use_sec) {
220 /* Disable SEC by setting nshards to 0 */
221 sec_opts.nshards = 0;
222 }
223
224 if (pa_shard_enable_hpa(tsd_tsdn(tsd_fetch()),
225 &g_shard_infra[i].pa_shard, &hpa_opts, &sec_opts)) {
226 fprintf(
227 stderr, "Failed to enable HPA on shard %d\n", i);
228 /* Clean up partially initialized shards */
229 cleanup_pa_infrastructure(num_shards);
230 return true;
231 }
232 }
233
234 printf("PA infrastructure configured: HPA=enabled, SEC=%s\n",
235 g_use_sec ? "enabled" : "disabled");
236
237 return false;
238 }
239
240 static void
241 cleanup_pa_infrastructure(int num_shards) {
242 if (g_shard_infra != NULL) {
243 for (int i = 0; i < num_shards; i++) {
244 pa_shard_destroy(
245 tsd_tsdn(tsd_fetch()), &g_shard_infra[i].pa_shard);
246 if (g_shard_infra[i].base != NULL) {
247 base_delete(tsd_tsdn(tsd_fetch()),
248 g_shard_infra[i].base);
249 }
250 }
251 free(g_shard_infra);
252 g_shard_infra = NULL;
253 }
254
255 if (g_shard_stats != NULL) {
256 free(g_shard_stats);
257 g_shard_stats = NULL;
258 }
259 }
260
261 static bool
262 parse_csv_line(const char *line, pa_event_t *event) {
263 /* Expected format: shard_ind,operation,size_or_alloc_index,is_frequent */
264 int operation;
265 int fields = sscanf(line, "%d,%d,%zu,%lu,%d", &event->shard_ind,
266 &operation, &event->size_or_alloc_index, &event->nsecs,
267 &event->is_frequent);
268
269 if (fields < 4) { /* is_frequent is optional */
270 return false;
271 }
272
273 if (fields == 4) {
274 event->is_frequent = 0; /* Default value */
275 }
276
277 if (operation == 0) {
278 event->operation = PA_ALLOC;
279 } else if (operation == 1) {
280 event->operation = PA_DALLOC;
281 } else {
282 return false;
283 }
284
285 return true;
286 }
287
288 static size_t
289 load_trace_file(const char *filename, pa_event_t **events, int *max_shard_id) {
290 FILE *file = fopen(filename, "r");
291 if (!file) {
292 fprintf(stderr, "Failed to open trace file: %s\n", filename);
293 return 0;
294 }
295
296 *events = malloc(MAX_ALLOCATIONS * sizeof(pa_event_t));
297 if (!*events) {
298 fclose(file);
299 return 0;
300 }
301
302 char line[MAX_LINE_LENGTH];
303 size_t count = 0;
304 *max_shard_id = 0;
305
306 /* Skip header line */
307 if (fgets(line, sizeof(line), file) == NULL) {
308 fclose(file);
309 free(*events);
310 return 0;
311 }
312
313 while (fgets(line, sizeof(line), file) && count < MAX_ALLOCATIONS) {
314 if (parse_csv_line(line, &(*events)[count])) {
315 if ((*events)[count].shard_ind > *max_shard_id) {
316 *max_shard_id = (*events)[count].shard_ind;
317 }
318 count++;
319 }
320 }
321
322 fclose(file);
323 printf("Loaded %zu events from %s\n", count, filename);
324 printf("Maximum shard ID found: %d\n", *max_shard_id);
325 return count;
326 }
327
328 static void
329 collect_hpa_stats(int shard_id, hpa_shard_stats_t *hpa_stats_out) {
330 /* Get tsdn for statistics collection */
331 tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
332
333 /* Clear the output structure */
334 memset(hpa_stats_out, 0, sizeof(hpa_shard_stats_t));
335
336 /* Check if this shard has HPA enabled */
337 if (!g_shard_infra[shard_id].pa_shard.ever_used_hpa) {
338 return;
339 }
340
341 /* Merge HPA statistics from the shard */
342 hpa_shard_stats_merge(
343 tsdn, &g_shard_infra[shard_id].pa_shard.hpa_shard, hpa_stats_out);
344 }
345
346 static void
347 print_shard_stats(int shard_id, size_t operation_count) {
348 if (!g_stats_output) {
349 return;
350 }
351
352 /* Collect HPA statistics */
353 hpa_shard_stats_t hpa_stats;
354 collect_hpa_stats(shard_id, &hpa_stats);
355 psset_stats_t *psset_stats = &hpa_stats.psset_stats;
356
357 /* Total pageslabs */
358 size_t total_pageslabs = psset_stats->merged.npageslabs;
359
360 /* Full pageslabs breakdown by hugification */
361 size_t full_pageslabs_non_huge =
362 psset_stats->full_slabs[0].npageslabs; /* [0] = non-hugified */
363 size_t full_pageslabs_huge =
364 psset_stats->full_slabs[1].npageslabs; /* [1] = hugified */
365 size_t full_pageslabs_total = full_pageslabs_non_huge
366 + full_pageslabs_huge;
367
368 /* Empty pageslabs breakdown by hugification */
369 size_t empty_pageslabs_non_huge =
370 psset_stats->empty_slabs[0].npageslabs; /* [0] = non-hugified */
371 size_t empty_pageslabs_huge =
372 psset_stats->empty_slabs[1].npageslabs; /* [1] = hugified */
373 size_t empty_pageslabs_total = empty_pageslabs_non_huge
374 + empty_pageslabs_huge;
375
376 /* Hugified pageslabs (full + empty + partial) */
377 size_t hugified_pageslabs = full_pageslabs_huge + empty_pageslabs_huge;
378 /* Add hugified partial slabs */
379 for (int i = 0; i < PSSET_NPSIZES; i++) {
380 hugified_pageslabs +=
381 psset_stats->nonfull_slabs[i][1].npageslabs;
382 }
383
384 /* Dirty bytes */
385 size_t dirty_bytes = psset_stats->merged.ndirty * PAGE;
386 uint64_t npurge_passes = hpa_stats.nonderived_stats.npurge_passes;
387 uint64_t npurges = hpa_stats.nonderived_stats.npurges;
388
389 assert(g_use_sec
390 || psset_stats->merged.nactive * PAGE
391 == g_shard_stats[shard_id].bytes_allocated);
392 /* Output enhanced stats with detailed breakdown */
393 fprintf(g_stats_output,
394 "%zu,%d,%lu,%lu,%lu,%zu,%zu,%zu,%zu,%zu,%zu,%zu,%zu,%zu,%lu,%lu,%lu"
395 ",%lu,%lu\n",
396 operation_count, shard_id, g_shard_stats[shard_id].alloc_count,
397 g_shard_stats[shard_id].dealloc_count,
398 g_shard_stats[shard_id].bytes_allocated, total_pageslabs,
399 full_pageslabs_total, empty_pageslabs_total, hugified_pageslabs,
400 full_pageslabs_non_huge, full_pageslabs_huge,
401 empty_pageslabs_non_huge, empty_pageslabs_huge, dirty_bytes,
402 hpa_stats.nonderived_stats.nhugifies,
403 hpa_stats.nonderived_stats.nhugify_failures,
404 hpa_stats.nonderived_stats.ndehugifies, npurge_passes, npurges);
405 fflush(g_stats_output);
406 }
407
408 static void
409 simulate_trace(
410 int num_shards, pa_event_t *events, size_t count, size_t stats_interval) {
411 uint64_t total_allocs = 0, total_deallocs = 0;
412 uint64_t total_allocated_bytes = 0;
413
414 printf("Starting simulation with %zu events across %d shards...\n",
415 count, num_shards);
416
417 for (size_t i = 0; i < count; i++) {
418 pa_event_t *event = &events[i];
419
420 /* Validate shard index */
421 if (event->shard_ind >= num_shards) {
422 fprintf(stderr,
423 "Warning: Invalid shard index %d (max %d)\n",
424 event->shard_ind, num_shards - 1);
425 continue;
426 }
427
428 set_clock(event->nsecs);
429 switch (event->operation) {
430 case PA_ALLOC: {
431 size_t size = event->size_or_alloc_index;
432
433 /* Get tsdn and calculate parameters for PA */
434 tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
435 szind_t szind = sz_size2index(size);
436 bool slab =
437 event
438 ->is_frequent; /* Use frequent_reuse for slab */
439 bool deferred_work_generated = false;
440
441 /* Allocate using PA allocator */
442 edata_t *edata = pa_alloc(tsdn,
443 &g_shard_infra[event->shard_ind].pa_shard, size,
444 PAGE /* alignment */, slab, szind, false /* zero */,
445 false /* guarded */, &deferred_work_generated);
446
447 if (edata != NULL) {
448 /* Store allocation record */
449 g_alloc_records[g_alloc_counter].edata = edata;
450 g_alloc_records[g_alloc_counter].size = size;
451 g_alloc_records[g_alloc_counter].shard_ind =
452 event->shard_ind;
453 g_alloc_records[g_alloc_counter].active = true;
454 g_alloc_counter++;
455
456 /* Update shard-specific stats */
457 g_shard_stats[event->shard_ind].alloc_count++;
458 g_shard_stats[event->shard_ind]
459 .bytes_allocated += size;
460
461 total_allocs++;
462 total_allocated_bytes += size;
463 }
464 break;
465 }
466 case PA_DALLOC: {
467 size_t alloc_index = event->size_or_alloc_index;
468 if (alloc_index < g_alloc_counter
469 && g_alloc_records[alloc_index].active
470 && g_alloc_records[alloc_index].shard_ind
471 == event->shard_ind) {
472 /* Get tsdn for PA */
473 tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
474 bool deferred_work_generated = false;
475
476 /* Deallocate using PA allocator */
477 pa_dalloc(tsdn,
478 &g_shard_infra[event->shard_ind].pa_shard,
479 g_alloc_records[alloc_index].edata,
480 &deferred_work_generated);
481
482 /* Update shard-specific stats */
483 g_shard_stats[event->shard_ind].dealloc_count++;
484 g_shard_stats[event->shard_ind]
485 .bytes_allocated -=
486 g_alloc_records[alloc_index].size;
487
488 g_alloc_records[alloc_index].active = false;
489 total_deallocs++;
490 }
491 break;
492 }
493 }
494
495 /* Periodic stats output and progress reporting */
496 if (stats_interval > 0 && (i + 1) % stats_interval == 0) {
497 /* Print stats for all shards */
498 for (int j = 0; j < num_shards; j++) {
499 print_shard_stats(j, i + 1);
500 }
501 }
502 }
503
504 printf("\nSimulation completed:\n");
505 printf(" Total allocations: %lu\n", total_allocs);
506 printf(" Total deallocations: %lu\n", total_deallocs);
507 printf(" Total allocated: %lu bytes\n", total_allocated_bytes);
508 printf(" Active allocations: %lu\n", g_alloc_counter - total_deallocs);
509
510 /* Print final stats for all shards */
511 printf("\nFinal shard statistics:\n");
512 for (int i = 0; i < num_shards; i++) {
513 printf(
514 " Shard %d: Allocs=%lu, Deallocs=%lu, Active Bytes=%lu\n",
515 i, g_shard_stats[i].alloc_count,
516 g_shard_stats[i].dealloc_count,
517 g_shard_stats[i].bytes_allocated);
518
519 /* Final stats to file */
520 print_shard_stats(i, count);
521 }
522 }
523
524 static void
525 cleanup_remaining_allocations(int num_shards) {
526 size_t cleaned_up = 0;
527
528 printf("Cleaning up remaining allocations...\n");
529
530 for (size_t i = 0; i < g_alloc_counter; i++) {
531 if (g_alloc_records[i].active) {
532 int shard_ind = g_alloc_records[i].shard_ind;
533 if (shard_ind < num_shards) {
534 tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
535 bool deferred_work_generated = false;
536
537 pa_dalloc(tsdn,
538 &g_shard_infra[shard_ind].pa_shard,
539 g_alloc_records[i].edata,
540 &deferred_work_generated);
541
542 g_alloc_records[i].active = false;
543 cleaned_up++;
544 }
545 }
546 }
547
548 printf("Cleaned up %zu remaining allocations\n", cleaned_up);
549 }
550
551 static void
552 print_usage(const char *program) {
553 printf("Usage: %s [options] <trace_file.csv>\n", program);
554 printf("Options:\n");
555 printf(" -h, --help Show this help message\n");
556 printf(
557 " -o, --output FILE Output file for statistics (default: stdout)\n");
558 printf(" -s, --sec Use SEC (default)\n");
559 printf(" -p, --hpa-only Use HPA only (no SEC)\n");
560 printf(
561 " -i, --interval N Stats print interval (default: 100000, 0=disable)\n");
562 printf(
563 "\nTrace file format: shard_ind,operation,size_or_alloc_index,is_frequent\n");
564 printf(" - operation: 0=alloc, 1=dealloc\n");
565 printf(" - is_frequent: optional column\n");
566 }
567
568 int
569 main(int argc, char *argv[]) {
570 const char *trace_file = NULL;
571 const char *stats_output_file = NULL;
572 size_t stats_interval = 100000; /* Default stats print interval */
573 /* Parse command line arguments */
574 for (int i = 1; i < argc; i++) {
575 if (strcmp(argv[i], "-h") == 0
576 || strcmp(argv[i], "--help") == 0) {
577 print_usage(argv[0]);
578 return 0;
579 } else if (strcmp(argv[i], "-o") == 0
580 || strcmp(argv[i], "--output") == 0) {
581 if (i + 1 >= argc) {
582 fprintf(stderr,
583 "Error: %s requires an argument\n",
584 argv[i]);
585 return 1;
586 }
587 stats_output_file = argv[++i];
588 } else if (strcmp(argv[i], "-s") == 0
589 || strcmp(argv[i], "--sec") == 0) {
590 g_use_sec = true;
591 } else if (strcmp(argv[i], "-p") == 0
592 || strcmp(argv[i], "--hpa-only") == 0) {
593 g_use_sec = false;
594 } else if (strcmp(argv[i], "-i") == 0
595 || strcmp(argv[i], "--interval") == 0) {
596 if (i + 1 >= argc) {
597 fprintf(stderr,
598 "Error: %s requires an argument\n",
599 argv[i]);
600 return 1;
601 }
602 stats_interval = (size_t)atol(argv[++i]);
603 } else if (argv[i][0] != '-') {
604 trace_file = argv[i];
605 } else {
606 fprintf(stderr, "Unknown option: %s\n", argv[i]);
607 print_usage(argv[0]);
608 return 1;
609 }
610 }
611
612 if (!trace_file) {
613 fprintf(stderr, "Error: No trace file specified\n");
614 print_usage(argv[0]);
615 return 1;
616 }
617
618 printf("Trace file: %s\n", trace_file);
619 printf("Mode: %s\n", g_use_sec ? "PA with SEC" : "HPA only");
620
621 /* Open stats output file */
622 if (stats_output_file) {
623 g_stats_output = fopen(stats_output_file, "w");
624 if (!g_stats_output) {
625 fprintf(stderr,
626 "Failed to open stats output file: %s\n",
627 stats_output_file);
628 return 1;
629 }
630 printf("Stats output: %s\n", stats_output_file);
631
632 /* Write CSV header */
633 fprintf(g_stats_output,
634 "operation_count,shard_id,alloc_count,dealloc_count,active_bytes,"
635 "total_pageslabs,full_pageslabs_total,empty_pageslabs_total,hugified_pageslabs,"
636 "full_pageslabs_non_huge,full_pageslabs_huge,"
637 "empty_pageslabs_non_huge,empty_pageslabs_huge,"
638 "dirty_bytes,nhugifies,nhugify_failures,ndehugifies,"
639 "npurge_passes,npurges\n");
640 }
641
642 /* Load trace data and determine max number of arenas */
643 pa_event_t *events;
644 int max_shard_id;
645 size_t event_count = load_trace_file(
646 trace_file, &events, &max_shard_id);
647 if (event_count == 0) {
648 if (g_stats_output)
649 fclose(g_stats_output);
650 return 1;
651 }
652
653 int num_shards = max_shard_id + 1; /* shard IDs are 0-based */
654 if (num_shards > MAX_ARENAS) {
655 fprintf(stderr, "Error: Too many arenas required (%d > %d)\n",
656 num_shards, MAX_ARENAS);
657 free(events);
658 if (g_stats_output)
659 fclose(g_stats_output);
660 return 1;
661 }
662
663 /* Allocate allocation tracking array */
664 g_alloc_records = malloc(event_count * sizeof(allocation_record_t));
665
666 if (!g_alloc_records) {
667 fprintf(
668 stderr, "Failed to allocate allocation tracking array\n");
669 free(events);
670 if (g_stats_output) {
671 fclose(g_stats_output);
672 }
673 return 1;
674 }
675
676 /* Initialize PA infrastructure */
677 if (initialize_pa_infrastructure(num_shards)) {
678 fprintf(stderr, "Failed to initialize PA infrastructure\n");
679 free(events);
680 free(g_alloc_records);
681 if (g_stats_output) {
682 fclose(g_stats_output);
683 }
684 return 1;
685 }
686
687 /* Run simulation */
688 simulate_trace(num_shards, events, event_count, stats_interval);
689
690 /* Clean up remaining allocations */
691 cleanup_remaining_allocations(num_shards);
692
693 /* Cleanup PA infrastructure */
694 cleanup_pa_infrastructure(num_shards);
695
696 /* Cleanup */
697 free(g_alloc_records);
698 free(events);
699
700 if (g_stats_output) {
701 fclose(g_stats_output);
702 printf("Statistics written to: %s\n", stats_output_file);
703 }
704
705 return 0;
706 }
707