master
c 681 lines 20.9 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "uuidmap.h"
4
5 struct uuidmap_entry {
6 nd_uuid_t uuid;
7 REFCOUNT refcount;
8 };
9
10 struct uuidmap_partition {
11 Pvoid_t uuid_to_id; // JudyL: UUID string -> ID
12 Pvoid_t id_to_uuid; // JudyL: ID -> UUID binary
13 UUIDMAP_ID next_id; // Only use lower bits
14 RW_SPINLOCK spinlock;
15
16 int64_t memory;
17 int32_t entries;
18 };
19
20 static struct {
21 struct uuidmap_partition p[UUIDMAP_PARTITIONS];
22 ARAL *ar;
23 } uuid_map = { 0 };
24
25 static struct aral_statistics uuidmap_stats = { 0 };
26 struct aral_statistics *uuidmap_aral_statistics(void) { return &uuidmap_stats; }
27
28 size_t uuidmap_memory(void) {
29 size_t memory = 0;
30
31 for(size_t i = 0; i < _countof(uuid_map.p) ;i++) {
32 rw_spinlock_read_lock(&uuid_map.p[i].spinlock);
33 memory += uuid_map.p[i].memory;
34 rw_spinlock_read_unlock(&uuid_map.p[i].spinlock);
35 }
36
37 return memory;
38 }
39
40 size_t uuidmap_free_bytes(void) {
41 return aral_free_bytes_from_stats(&uuidmap_stats);
42 }
43
44 static void uuidmap_init_aral(void) {
45 static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
46
47 if(!uuid_map.ar) {
48 spinlock_lock(&spinlock);
49 if(!uuid_map.ar) {
50 uuid_map.ar = aral_create(
51 "uuidmap",
52 sizeof(struct uuidmap_entry),
53 0,
54 0,
55 &uuidmap_stats,
56 NULL, NULL, false, false, true);
57 }
58 spinlock_unlock(&spinlock);
59 }
60 }
61
62 static UUIDMAP_ID get_next_id_unsafe(struct uuidmap_partition *partition) {
63 // Check if we've reached the maximum ID value
64 if (unlikely(partition->next_id >= 0x1FFFFFFF))
65 fatal("UUIDMAP: Maximum ID limit reached for partition %u. UUIDs exhausted.",
66 (unsigned int)(partition - uuid_map.p));
67
68 // Simply increment and return the next ID
69 return uuidmap_make_id(partition - uuid_map.p, ++partition->next_id);
70 }
71
72 static inline UUIDMAP_ID uuidmap_acquire_by_uuid(const nd_uuid_t uuid) {
73 UUIDMAP_ID id = 0;
74
75 uint8_t partition = uuid_to_uuidmap_partition(uuid);
76
77 // try to find it in the JudyHS - we may have it already
78 rw_spinlock_read_lock(&uuid_map.p[partition].spinlock);
79 Pvoid_t *PValue = JudyHSGet(uuid_map.p[partition].uuid_to_id, (void *)uuid, sizeof(nd_uuid_t));
80 if(PValue == PJERR)
81 fatal("UUIDMAP: corrupted JudyHS array");
82
83 if(PValue && *PValue) {
84 // it is found
85
86 id = *(UUIDMAP_ID *)PValue;
87
88 PValue = JudyLGet(uuid_map.p[partition].id_to_uuid, id, PJE0);
89 if (!PValue || PValue == PJERR)
90 fatal("UUIDMAP: corrupted JudyL array");
91
92 struct uuidmap_entry *ue = *PValue;
93 if(!refcount_acquire(&ue->refcount))
94 id = 0;
95 }
96
97 rw_spinlock_read_unlock(&uuid_map.p[partition].spinlock);
98 return id;
99 }
100
101 UUIDMAP_ID uuidmap_create(const nd_uuid_t uuid) {
102 UUIDMAP_ID id = uuidmap_acquire_by_uuid(uuid);
103 if(id != 0) return id;
104
105 uuidmap_init_aral();
106
107 // we didn't find it - let's add it
108
109 uint8_t partition = uuid_to_uuidmap_partition(uuid);
110
111 JudyAllocThreadPulseReset();
112
113 Pvoid_t *PValue;
114 while(true) {
115 rw_spinlock_write_lock(&uuid_map.p[partition].spinlock);
116
117 PValue = JudyHSIns(&uuid_map.p[partition].uuid_to_id, (void *)uuid, sizeof(nd_uuid_t), PJE0);
118 if (!PValue || PValue == PJERR)
119 fatal("UUIDMAP: corrupted JudyHS array");
120
121 // If value exists, return it
122 if (*PValue != 0) {
123 id = (UUIDMAP_ID)(uintptr_t)*PValue;
124
125 PValue = JudyLGet(uuid_map.p[partition].id_to_uuid, id, PJE0);
126 if (!PValue || PValue == PJERR)
127 fatal("UUIDMAP: corrupted JudyL array");
128
129 struct uuidmap_entry *ue = *PValue;
130 if (!refcount_acquire(&ue->refcount)) {
131 rw_spinlock_write_unlock(&uuid_map.p[partition].spinlock);
132 continue;
133 }
134
135 uuid_map.p[partition].memory += JudyAllocThreadPulseGetAndReset();
136 rw_spinlock_write_unlock(&uuid_map.p[partition].spinlock);
137 return id;
138 }
139 else
140 break;
141 }
142
143 id = get_next_id_unsafe(&uuid_map.p[partition]);
144 *(UUIDMAP_ID *)PValue = id;
145
146 // Store ID -> UUID mapping
147 PValue = JudyLIns(&uuid_map.p[partition].id_to_uuid, id, PJE0);
148 if (!PValue || PValue == PJERR)
149 fatal("UUIDMAP: corrupted JudyL array");
150
151 struct uuidmap_entry *ue = aral_mallocz(uuid_map.ar);
152 nd_uuid_copy(ue->uuid, uuid);
153 ue->refcount = 1;
154 *PValue = ue;
155
156 uuid_map.p[partition].entries++;
157 uuid_map.p[partition].memory += sizeof(*ue);
158
159 uuid_map.p[partition].memory += JudyAllocThreadPulseGetAndReset();
160 rw_spinlock_write_unlock(&uuid_map.p[partition].spinlock);
161 return id;
162 }
163
164 static struct uuidmap_entry *get_entry_by_id(UUIDMAP_ID id) {
165 if(id == 0) return NULL;
166
167 uint8_t partition = uuidmap_id_to_partition(id);
168
169 rw_spinlock_read_lock(&uuid_map.p[partition].spinlock);
170
171 Pvoid_t *PValue = JudyLGet(uuid_map.p[partition].id_to_uuid, id, PJE0);
172 if (PValue == PJERR)
173 fatal("UUIDMAP: corrupted JudyL array");
174
175 struct uuidmap_entry *ue = PValue ? *PValue : NULL;
176
177 rw_spinlock_read_unlock(&uuid_map.p[partition].spinlock);
178
179 return ue;
180 }
181
182 void uuidmap_free(UUIDMAP_ID id) {
183 struct uuidmap_entry *ue = get_entry_by_id(id);
184
185 if(ue && refcount_release_and_acquire_for_deletion(&ue->refcount)) {
186 JudyAllocThreadPulseReset();
187 uint8_t partition = uuidmap_id_to_partition(id);
188 rw_spinlock_write_lock(&uuid_map.p[partition].spinlock);
189
190 int rc;
191 rc = JudyHSDel(&uuid_map.p[partition].uuid_to_id, (void *)ue->uuid, sizeof(nd_uuid_t), PJE0);
192 if(unlikely(!rc))
193 fatal("UUIDMAP: cannot delete UUID from JudyHS");
194
195 rc = JudyLDel(&uuid_map.p[partition].id_to_uuid, id, PJE0);
196 if(unlikely(!rc))
197 fatal("UUIDMAP: cannot delete ID from JudyL");
198
199 uuid_map.p[partition].memory -= sizeof(*ue);
200 uuid_map.p[partition].entries--;
201
202 uuid_map.p[partition].memory += JudyAllocThreadPulseGetAndReset();
203 rw_spinlock_write_unlock(&uuid_map.p[partition].spinlock);
204
205 aral_freez(uuid_map.ar, ue);
206 }
207 }
208
209 nd_uuid_t *uuidmap_uuid_ptr(UUIDMAP_ID id) {
210 struct uuidmap_entry *ue = get_entry_by_id(id);
211 return ue ? &ue->uuid : NULL;
212 }
213
214 nd_uuid_t *uuidmap_uuid_ptr_and_dup(UUIDMAP_ID id) {
215 struct uuidmap_entry *ue = get_entry_by_id(id);
216
217 if(ue && refcount_acquire(&ue->refcount))
218 return &ue->uuid;
219
220 return NULL;
221 }
222
223 bool uuidmap_uuid(UUIDMAP_ID id, nd_uuid_t out_uuid) {
224 nd_uuid_t *uuid = uuidmap_uuid_ptr(id);
225
226 if(!uuid) {
227 nd_uuid_clear(out_uuid);
228 return false;
229 }
230
231 uuid_copy(out_uuid, *uuid);
232 return true;
233 }
234
235 ND_UUID uuidmap_get(UUIDMAP_ID id) {
236 ND_UUID uuid;
237 uuidmap_uuid(id, uuid.uuid);
238 return uuid;
239 }
240
241 UUIDMAP_ID uuidmap_dup(UUIDMAP_ID id) {
242 struct uuidmap_entry *ue = get_entry_by_id(id);
243
244 if(!ue || !refcount_acquire(&ue->refcount))
245 fatal("UUIDMAP: id %u does not exist, or cannot be acquired, in %s", id, __FUNCTION__ );
246
247 return id;
248 }
249
250 size_t uuidmap_destroy(void) {
251 size_t referenced = 0;
252
253 // Traverse all partitions
254 for (size_t partition = 0; partition < UUIDMAP_PARTITIONS; partition++) {
255 // Lock the partition to prevent new entries while we're cleaning up
256 rw_spinlock_write_lock(&uuid_map.p[partition].spinlock);
257
258 Pvoid_t uuid_to_id = uuid_map.p[partition].uuid_to_id;
259 Pvoid_t id_to_uuid = uuid_map.p[partition].id_to_uuid;
260
261 // Process all entries in the id_to_uuid map
262 Word_t id_index = 0;
263 Pvoid_t *id_pvalue;
264
265 for (id_pvalue = JudyLFirst(id_to_uuid, &id_index, PJE0);
266 id_pvalue != NULL && id_pvalue != PJERR;
267 id_pvalue = JudyLNext(id_to_uuid, &id_index, PJE0)) {
268
269 if (!(*id_pvalue))
270 continue;
271
272 struct uuidmap_entry *ue = *id_pvalue;
273
274 // Try to acquire for deletion
275 if (!refcount_acquire_for_deletion(&ue->refcount))
276 referenced++;
277
278 aral_freez(uuid_map.ar, ue);
279 }
280
281 // Free all Judy arrays
282 JudyHSFreeArray(&uuid_to_id, PJE0);
283 JudyLFreeArray(&id_to_uuid, PJE0);
284
285 // Reset partition data
286 memset(&uuid_map.p[partition], 0, sizeof(uuid_map.p[partition]));
287
288 rw_spinlock_write_unlock(&uuid_map.p[partition].spinlock);
289 }
290
291 // Destroy ARAL
292 if (uuid_map.ar) {
293 aral_destroy(uuid_map.ar);
294 uuid_map.ar = NULL;
295 }
296
297 memset(&uuid_map, 0, sizeof(uuid_map));
298 return referenced;
299 }
300
301 // --------------------------------------------------------------------------------------------------------------------
302
303 static volatile bool stop_flag = false;
304
305 typedef struct thread_stats {
306 size_t creates;
307 size_t finds;
308 size_t dups;
309 size_t frees;
310 size_t cycles;
311 } THREAD_STATS;
312
313 static void concurrent_test_thread(void *arg) {
314 THREAD_STATS *stats = arg;
315 nd_uuid_t test_uuid = {
316 0x12, 0x34, 0x56, 0x78,
317 0x9a, 0xbc, 0xde, 0xf0,
318 0x12, 0x34, 0x56, 0x78,
319 0x9a, 0xbc, 0xde, 0xf0
320 };
321
322 while(!__atomic_load_n(&stop_flag, __ATOMIC_RELAXED)) {
323 // 1. Create UUID (refcount 1)
324 UUIDMAP_ID id = uuidmap_create(test_uuid);
325 if(!id) continue;
326 stats->creates++;
327
328 // 2. Find its pointer
329 nd_uuid_t *uuid_ptr = uuidmap_uuid_ptr(id);
330 if(!uuid_ptr) {
331 fprintf(stderr, "ERROR: Cannot find UUID we just created\n");
332 break;
333 }
334 stats->finds++;
335
336 // 3. Dup it (refcount 2)
337 UUIDMAP_ID id2 = uuidmap_dup(id);
338 if(!id2) {
339 fprintf(stderr, "ERROR: Cannot dup UUID\n");
340 break;
341 }
342 stats->dups++;
343
344 // 4. Free it once (refcount 1)
345 uuidmap_free(id);
346 stats->frees++;
347
348 // 5. Find its pointer again
349 uuid_ptr = uuidmap_uuid_ptr(id2);
350 if(!uuid_ptr) {
351 fprintf(stderr, "ERROR: Cannot find UUID after first free\n");
352 break;
353 }
354 stats->finds++;
355
356 // 6. Free it twice (should delete)
357 uuidmap_free(id2);
358 stats->frees++;
359
360 stats->cycles++;
361 }
362 }
363
364 static int uuidmap_concurrent_unittest(void) {
365 enum { UUIDMAP_UNITTEST_THREADS = 4, UUIDMAP_UNITTEST_SECONDS = 5 };
366 fprintf(stderr, "\nTesting concurrent UUID Map access with %d threads for %d seconds...\n", UUIDMAP_UNITTEST_THREADS, UUIDMAP_UNITTEST_SECONDS);
367 int errors = 0;
368
369 THREAD_STATS stats[UUIDMAP_UNITTEST_THREADS];
370 memset(stats, 0, sizeof(stats));
371
372 ND_THREAD *threads[UUIDMAP_UNITTEST_THREADS];
373
374 // Start threads
375 __atomic_store_n(&stop_flag, false, __ATOMIC_RELAXED);
376
377 for(int i = 0; i < UUIDMAP_UNITTEST_THREADS; i++) {
378 char thread_name[32];
379 snprintf(thread_name, sizeof(thread_name), "UUID-TEST-%d", i);
380 threads[i] = nd_thread_create(
381 thread_name,
382 NETDATA_THREAD_OPTION_DONT_LOG,
383 concurrent_test_thread,
384 &stats[i]);
385 }
386
387 // Let it run for 5 seconds
388 sleep_usec(UUIDMAP_UNITTEST_SECONDS * USEC_PER_SEC);
389
390 // Stop threads
391 __atomic_store_n(&stop_flag, true, __ATOMIC_RELEASE);
392
393 // Wait for threads
394 for(int i = 0; i < UUIDMAP_UNITTEST_THREADS; i++)
395 nd_thread_join(threads[i]);
396
397 // Print statistics
398 size_t total_cycles = 0;
399 for(int i = 0; i < UUIDMAP_UNITTEST_THREADS; i++) {
400 fprintf(stderr, "Thread %d stats:\n"
401 " Cycles completed : %zu\n"
402 " Creates : %zu\n"
403 " Finds : %zu\n"
404 " Dups : %zu\n"
405 " Frees : %zu\n",
406 i,
407 stats[i].cycles,
408 stats[i].creates,
409 stats[i].finds,
410 stats[i].dups,
411 stats[i].frees);
412
413 total_cycles += stats[i].cycles;
414 }
415
416 fprintf(stderr, "\nTotal cycles completed: %zu (%.2f cycles/sec)\n",
417 total_cycles,
418 (double)total_cycles / 5.0);
419
420 return errors;
421 }
422
423 int uuidmap_unittest(void) {
424 fprintf(stderr, "\nTesting UUID Map...\n");
425
426 const size_t ENTRIES = 100000;
427 int errors = uuidmap_concurrent_unittest();
428
429 struct test_entry {
430 nd_uuid_t uuid;
431 UUIDMAP_ID id;
432 };
433
434 struct test_entry *entries = mallocz(sizeof(struct test_entry) * ENTRIES);
435
436 fprintf(stderr, "Generating and testing %zu entries...\n", ENTRIES);
437
438 usec_t start_time = now_monotonic_usec();
439 size_t step = ENTRIES / 100;
440 size_t next_step = step;
441
442 for(size_t i = 0; i < ENTRIES; i++) {
443 if (i >= next_step) {
444 fprintf(stderr, ".");
445 next_step += step;
446 }
447
448 uuid_generate_random(entries[i].uuid);
449 char uuid_str[UUID_STR_LEN];
450 uuid_unparse_lower(entries[i].uuid, uuid_str);
451
452 // Test 1: Should not exist yet
453 UUIDMAP_ID id = uuidmap_acquire_by_uuid(entries[i].uuid);
454 if(id != 0) {
455 fprintf(stderr, "\nERROR [%zu]: UUID found before adding it"
456 "\n UUID: %s"
457 "\n Got ID: %u (expected: 0)\n",
458 i, uuid_str, id);
459 errors++;
460 }
461
462 // Test 2: Create it
463 id = uuidmap_create(entries[i].uuid);
464 if(id == 0) {
465 fprintf(stderr, "\nERROR [%zu]: Failed to create UUID mapping"
466 "\n UUID: %s\n",
467 i, uuid_str);
468 errors++;
469 continue;
470 }
471
472 // Test 3: Create again, should return same id
473 UUIDMAP_ID id2 = uuidmap_create(entries[i].uuid);
474 if(id2 != id) {
475 fprintf(stderr, "\nERROR [%zu]: Second create returned different ID"
476 "\n UUID: %s"
477 "\n First ID: %u"
478 "\n Second ID: %u\n",
479 i, uuid_str, id, id2);
480 errors++;
481 }
482
483 // Test 4: Get UUID and verify
484 nd_uuid_t test_uuid;
485 if(!uuidmap_uuid(id, test_uuid)) {
486 fprintf(stderr, "\nERROR [%zu]: Failed to get UUID for valid ID"
487 "\n UUID: %s"
488 "\n ID: %u\n",
489 i, uuid_str, id);
490 errors++;
491 }
492 else {
493 char test_uuid_str[UUID_STR_LEN];
494 uuid_unparse_lower(test_uuid, test_uuid_str);
495 if(uuid_compare(test_uuid, entries[i].uuid) != 0) {
496 fprintf(stderr, "\nERROR [%zu]: Retrieved UUID doesn't match original"
497 "\n Original UUID: %s"
498 "\n Retrieved UUID: %s"
499 "\n ID: %u\n",
500 i, uuid_str, test_uuid_str, id);
501 errors++;
502 }
503 }
504
505 // Test 5: Free once (decrease refcount)
506 uuidmap_free(id);
507
508 // Test 6: Should still exist
509 if(!uuidmap_uuid(id, test_uuid)) {
510 fprintf(stderr, "\nERROR [%zu]: UUID disappeared after first free"
511 "\n UUID: %s"
512 "\n ID: %u\n",
513 i, uuid_str, id);
514 errors++;
515 }
516 else {
517 char test_uuid_str[UUID_STR_LEN];
518 uuid_unparse_lower(test_uuid, test_uuid_str);
519 if(uuid_compare(test_uuid, entries[i].uuid) != 0) {
520 fprintf(stderr, "\nERROR [%zu]: Retrieved UUID doesn't match after first free"
521 "\n Original UUID: %s"
522 "\n Retrieved UUID: %s"
523 "\n ID: %u\n",
524 i, uuid_str, test_uuid_str, id);
525 errors++;
526 }
527 }
528
529 // Test 7: Free again (should delete)
530 uuidmap_free(id);
531
532 // Test 8: Should be gone
533 if(uuidmap_uuid_ptr(id) != NULL) {
534 char curr_uuid_str[UUID_STR_LEN];
535 nd_uuid_t *curr_uuid = uuidmap_uuid_ptr(id);
536 if(curr_uuid)
537 uuid_unparse_lower(*curr_uuid, curr_uuid_str);
538
539 fprintf(stderr, "\nERROR [%zu]: UUID still exists after second free"
540 "\n Original UUID: %s"
541 "\n Current UUID: %s"
542 "\n ID: %u\n",
543 i, uuid_str, curr_uuid_str, id);
544
545 errors++;
546 }
547
548 // Test 9: Create again for phase 2
549 id = uuidmap_create(entries[i].uuid);
550 if(id == 0) {
551 fprintf(stderr, "\nERROR [%zu]: Failed to recreate UUID mapping"
552 "\n UUID: %s\n",
553 i, uuid_str);
554 errors++;
555 continue;
556 }
557
558 entries[i].id = id;
559 }
560
561 usec_t end_time = now_monotonic_usec();
562 fprintf(stderr, "\nPhase 1 completed in %.2f seconds with %d errors\n",
563 (double)(end_time - start_time) / (double)USEC_PER_SEC, errors);
564
565 // BENCHMARK while we have all entries loaded
566 if(errors == 0) {
567 fprintf(stderr, "\nBenchmarking UUID retrievals...\n");
568
569 // First benchmark: uuidmap_uuid_ptr()
570 size_t successful = 0;
571 usec_t start_ut = now_monotonic_usec();
572
573 for(size_t i = 0; i < ENTRIES; i++) {
574 nd_uuid_t *uuid_ptr = uuidmap_uuid_ptr(entries[i].id);
575 if(uuid_ptr && uuid_compare(*uuid_ptr, entries[i].uuid) == 0)
576 successful++;
577 }
578
579 usec_t end_ut = now_monotonic_usec();
580 double secs = (double)(end_ut - start_ut) / USEC_PER_SEC;
581 double ops = (double)successful / secs;
582
583 fprintf(stderr, "uuidmap_uuid_ptr() : %.2f ops/sec (%.2f usec/op)\n",
584 ops, (double)(end_ut - start_ut) / (double)successful);
585
586 // Second benchmark: uuidmap_get_by_uuid()
587 successful = 0;
588 start_ut = now_monotonic_usec();
589
590 for(size_t i = 0; i < ENTRIES; i++) {
591 UUIDMAP_ID id = uuidmap_acquire_by_uuid(entries[i].uuid);
592 if(id != 0) {
593 successful++;
594 uuidmap_free(id); // Must free since get_by_uuid increases refcount
595 }
596 }
597
598 end_ut = now_monotonic_usec();
599 secs = (double)(end_ut - start_ut) / USEC_PER_SEC;
600 ops = (double)successful / secs;
601
602 fprintf(stderr, "uuidmap_acquire_by_uuid(): %.2f ops/sec (%.2f usec/op)\n",
603 ops, (double)(end_ut - start_ut) / (double)successful);
604 }
605
606 // Phase 2: Delete everything
607 fprintf(stderr, "\nDeleting all entries...\n");
608 start_time = now_monotonic_usec();
609 next_step = step;
610
611 for(size_t i = 0; i < ENTRIES; i++) {
612 if (i >= next_step) {
613 fprintf(stderr, ".");
614 next_step += step;
615 }
616
617 UUIDMAP_ID id = entries[i].id;
618 char uuid_str[UUID_STR_LEN];
619 uuid_unparse_lower(entries[i].uuid, uuid_str);
620
621 // Test 1: Should exist
622 nd_uuid_t *uuid_ptr = uuidmap_uuid_ptr(id);
623 if(!uuid_ptr) {
624 fprintf(stderr, "\nERROR [%zu]: UUID not found before deletion"
625 "\n UUID: %s"
626 "\n ID: %u\n",
627 i, uuid_str, id);
628 errors++;
629 continue;
630 }
631
632 char current_uuid_str[UUID_STR_LEN];
633 uuid_unparse_lower(*uuid_ptr, current_uuid_str);
634 if(uuid_compare(*uuid_ptr, entries[i].uuid) != 0) {
635 fprintf(stderr, "\nERROR [%zu]: Retrieved UUID doesn't match before deletion"
636 "\n Original UUID: %s"
637 "\n Current UUID: %s"
638 "\n ID: %u\n",
639 i, uuid_str, current_uuid_str, id);
640 errors++;
641 }
642
643 // Test 2: Create again
644 UUIDMAP_ID id2 = uuidmap_create(entries[i].uuid);
645 if(id2 != id) {
646 fprintf(stderr, "\nERROR [%zu]: Recreation returned different ID"
647 "\n UUID: %s"
648 "\n Original ID: %u"
649 "\n New ID: %u\n",
650 i, uuid_str, id, id2);
651 errors++;
652 }
653
654 // Test 3 & 4: Free three times (one extra from benchmark)
655 uuidmap_free(id);
656 uuidmap_free(id);
657 uuidmap_free(id);
658
659 // Test 5: Should be gone
660 uuid_ptr = uuidmap_uuid_ptr(id);
661 if(uuid_ptr != NULL) {
662 char remaining_uuid_str[UUID_STR_LEN];
663 uuid_unparse_lower(*uuid_ptr, remaining_uuid_str);
664 fprintf(stderr, "\nERROR [%zu]: UUID still exists after final deletion"
665 "\n Original UUID: %s"
666 "\n Remaining UUID: %s"
667 "\n ID: %u\n",
668 i, uuid_str, remaining_uuid_str, id);
669 errors++;
670 }
671 }
672
673 end_time = now_monotonic_usec();
674 fprintf(stderr, "\nPhase 2 completed in %.2f seconds with %d errors\n",
675 (double)(end_time - start_time) / (double)USEC_PER_SEC, errors);
676
677 freez(entries);
678
679 fprintf(stderr, "\nUUID Map test completed with %d total errors\n", errors);
680 return errors;
681 }