* gcc-interface/trans.c (convert_with_check): Use a custom base type
[official-gcc.git] / libcilkrts / runtime / record-replay.cpp
blobd92d28fc233fae7bc35a0952d18712f4cc51bf3e
1 /* record-replay.cpp -*-C++-*-
3 *************************************************************************
5 * Copyright (C) 2012-2016, Intel Corporation
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 * * Neither the name of Intel Corporation nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
29 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
32 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
35 * *********************************************************************
37 * PLEASE NOTE: This file is a downstream copy of a file mainitained in
38 * a repository at cilkplus.org. Changes made to this file that are not
39 * submitted through the contribution process detailed at
40 * http://www.cilkplus.org/submit-cilk-contribution will be lost the next
41 * time that a new version is released. Changes only submitted to the
42 * GNU compiler collection or posted to the git repository at
43 * https://bitbucket.org/intelcilkruntime/intel-cilk-runtime.git are
44 * not tracked.
46 * We welcome your contributions to this open source project. Thank you
47 * for your assistance in helping us improve Cilk Plus.
49 **************************************************************************/
52 * Implementation of the record/replay functionality for Cilk Plus
55 #include <cstring>
56 #include <vector>
57 #include <stdlib.h>
59 // clang is really strict about printf formats, so use the annoying integer
60 // printf macros. Unfortunately they're not available on Windows (or on VxWorks)
61 #if defined(_WIN32) || defined(_WRS_KERNEL)
62 #define PRIu64 "llu"
63 #else
64 #define __STDC_FORMAT_MACROS 1
65 #include <inttypes.h>
66 #endif
68 #include "record-replay.h"
69 #include "bug.h"
70 #include "internal/abi.h"
71 #include "local_state.h"
72 #include "full_frame.h"
73 #include "global_state.h"
74 #include "cilk_malloc.h"
75 #include "os.h" // for cilkos_error()
77 #if RECORD_ON_REPLAY
78 #pragma message ("*** Record on Replay is enabled!")
79 #endif
81 // Defined to write sequence number to the logs. Note that you cannot
82 // diff logs with sequence numbers because the numbers may increment in
83 // different orders.
84 //#define INCLUDE_SEQUENCE_NUMBER 1
86 const int PED_VERSION = 1; // Log recording version
88 // Log types
89 enum ped_type_t
91 ped_type_unknown,
92 ped_type_steal,
93 ped_type_sync,
94 ped_type_orphaned,
95 ped_type_last // Flags end of the list
98 // Log type strings
99 #define PED_TYPE_STR_STEAL "Steal"
100 #define PED_TYPE_STR_SYNC "Sync"
101 #define PED_TYPE_STR_WORKERS "Workers"
102 #define PED_TYPE_STR_ORPHANED "Orphaned"
104 #define PED_TYPE_SIZE 16 // Buffer size for the type of pedigree. Must
105 // hold largest pedigree record type string.
106 #define PEDIGREE_BUFF_SIZE 512 // Buffer size for the string representation
107 // of a pedigree.
110 * Data we store for a replay log entry
112 typedef struct replay_entry_t
114 uint64_t *m_reverse_pedigree; /**< Reverse pedigree for replay log entry */
115 ped_type_t m_type; /**< Type of replay log entry */
116 int16_t m_pedigree_len; /**< Number of terms in reverse pedigree */
117 int16_t m_value; /**< Victim for STEALs, 0 if matching steal found for ORPHANs */
120 * Load data read from the log into the entry
122 bool load(const char *type, const char *pedigee_str, int32_t value1, int32_t value2)
124 // Convert the type into an enum
125 if (0 == strcmp(type, PED_TYPE_STR_STEAL))
127 m_type = ped_type_steal;
128 m_value = (int16_t)value1; // Victim
130 else
132 m_value = -1; // Victim not valid
133 if (0 == strcmp(type, PED_TYPE_STR_SYNC))
134 m_type = ped_type_sync;
135 else if (0 == strcmp(type, PED_TYPE_STR_ORPHANED))
136 m_type = ped_type_orphaned;
137 else
139 m_type = ped_type_unknown;
140 return false;
144 // Parse the pedigree
145 m_pedigree_len = 0;
147 const char *p = pedigee_str;
148 char *end;
150 uint64_t temp_pedigree[PEDIGREE_BUFF_SIZE/2];
152 while(1)
154 temp_pedigree[m_pedigree_len++] = (uint64_t)strtol(p, &end, 10);
155 if ('\0' == *end)
156 break;
157 p = end + 1;
160 // Allocate memory to hold the pedigree.
161 // Copy the pedigree in reverse order since that's the order we'll
162 // traverse it
163 m_reverse_pedigree =
164 (uint64_t *)__cilkrts_malloc(sizeof(int64_t) * m_pedigree_len);
165 for (int n = 0; n < m_pedigree_len; n++)
166 m_reverse_pedigree[n] = temp_pedigree[(m_pedigree_len - 1) - n];
168 return true;
172 * Match this entry against the data supplied. This includes walking the
173 * pedigree from the specified node.
175 bool match (ped_type_t type, const __cilkrts_pedigree *node, int victim = -1)
177 int i = 0;
179 // If the type isn't what they're seeking, we don't have a match
180 if (type != m_type)
181 return false;
183 // If we're looking for a STEAL, then the victim must match
184 if ((type == ped_type_steal) && (victim != m_value))
185 return false;
187 // Compare the current pedigree against what was recorded
188 while ((NULL != node) && (i < m_pedigree_len))
190 // If we've got a pedigree rank difference, then we don't have
191 // a match
192 if (node->rank != m_reverse_pedigree[i])
193 return false;
194 node = node->parent;
195 i++;
198 // Make sure we exhausted both the pedigree chain and the recorded
199 // pedigree
200 return ((NULL == node) && (i == m_pedigree_len));
204 * Advance to the next entry, skipping any ORPHANED records we didn't see
205 * a matching STEAL for
207 replay_entry_t *next_entry()
209 replay_entry_t *entry = this;
211 // You can't go beyond the end
212 if (ped_type_last == entry->m_type)
213 return entry;
215 // Advance to the next entry
216 entry++;
218 // Skip any ORPHANED records that don't have a matching steal. We
219 // initialized the value field to -1 for ORPHANED. After loading all
220 // the log data, we iterated through all the STEAL records setting the
221 // matching ORPHANED record's value field to 0. So if an ORPHANED
222 // record's value field is still -1, it doesn't have a matching STEAL
223 // record, and I don't know why we chose not to return from the
224 // spawned function.
225 while ((ped_type_orphaned == entry->m_type) && (-1 == entry->m_value))
227 entry++;
230 return entry;
234 * Release any allocated resources
236 void unload()
238 __cilkrts_free(m_reverse_pedigree);
239 m_reverse_pedigree = NULL;
242 } replay_entry_t;
244 __CILKRTS_BEGIN_EXTERN_C
247 * Walk the pedigree and generate a string representation with underscores
248 * between terms. Currently does a recursive walk to generate a forward
249 * pedigree.
251 * @param p The buffer that is to be filled. Assumed to be PEDIGREE_BUFF_SIZE
252 * characters long
253 * @param pnode The initial pedigree term to be written.
255 * @return A pointer into the pedigree string buffer after a term has been
256 * written.
258 static
259 char * walk_pedigree_nodes(char *p, const __cilkrts_pedigree *pnode)
261 CILK_ASSERT(pnode);
262 if (pnode->parent)
264 p = walk_pedigree_nodes(p, pnode->parent);
265 p += cilk_snprintf_s(p, PEDIGREE_BUFF_SIZE, "%s", (char *)"_");
267 return p + cilk_snprintf_l(p, PEDIGREE_BUFF_SIZE, "%" PRIu64, pnode->rank);
271 * Write a record to a replay log file.
273 * @param w The worker we're writing the pedigree for.
274 * @param type The type of the pedigree record, as a string
275 * @param initial_node The initial pedigree node to be written, or NULL if
276 * there is no pedigree for this record type.
277 * @param i1 First integer value to be written to the record.
278 * @param i2 Second integer value to be written to the record. Only applies
279 * to STEAL records. Defaults to -1 (unused). The second value is always
280 * written to make parsing easier.
282 static
283 void write_to_replay_log (__cilkrts_worker *w, const char *type,
284 const __cilkrts_pedigree *initial_node,
285 int i1 = -1, int i2 = -1)
287 char pedigree[PEDIGREE_BUFF_SIZE];
289 // If we don't have an initial pedigree node, just use "0" to fill the slot
290 if (NULL == initial_node)
291 cilk_strcpy_s(pedigree, PEDIGREE_BUFF_SIZE, "0");
292 else
293 walk_pedigree_nodes(pedigree, initial_node);
295 #ifndef INCLUDE_SEQUENCE_NUMBER
296 // Simply write the record
297 fprintf(w->l->record_replay_fptr, "%s %s %d %d\n",
298 type, pedigree, i1, i2);
299 #else
300 // Write the record with a sequence number. The sequence number should
301 // always be the last term, and ignored on read
303 static long volatile seq_num = 0;
304 long write_num;
306 // Atomic increment functions are compiler/OS-specific
307 #ifdef _WIN32
308 write_num = _InterlockedIncrement(&seq_num);
309 #else /* GCC */
310 write_num = __sync_add_and_fetch(&seq_num, 1);
311 #endif // _WIN32
313 fprintf(w->l->record_replay_fptr, "%s %s %d %d %ld\n",
314 type, pedigree, i1, i2, write_num);
315 #endif // INCLUDE_SEQUENCE_NUMBER
317 fflush(w->l->record_replay_fptr);
321 * Record data for a successful steal.
323 * The pedigree for a STEAL record is the pedigree of the stolen frame.
325 * @note It's assumed that replay_record_steal() has already checked that we're
326 * recording a log and that the record/replay functionality has not been
327 * compiled out.
329 * @param w The worker stealing a frame.
330 * @param victim_id The ID of the worker which had it's frame stolen.
332 void replay_record_steal_internal(__cilkrts_worker *w, int32_t victim_id)
334 // Follow the pedigree chain using worker's stack frame
335 CILK_ASSERT(w->l->next_frame_ff);
336 CILK_ASSERT(w->l->next_frame_ff->call_stack);
338 // Record steal: STEAL pedigree victim_id thief_id
339 write_to_replay_log (w, PED_TYPE_STR_STEAL,
340 &(w->l->next_frame_ff->call_stack->parent_pedigree),
341 victim_id);
345 * Record data for the worker that continues from a sync
347 * The pedigree for a SYNC record is the pedigree at the sync.
349 * @note It's assumed that replay_record_sync() has already checked that we're
350 * recording a log and that the record/replay functionality has not been
351 * compiled out.
353 * @param w The worker continuing from a sync.
355 void replay_record_sync_internal(__cilkrts_worker *w)
357 // Record sync: SYNC pedigree last_worker_id
358 write_to_replay_log (w, PED_TYPE_STR_SYNC, &w->pedigree);
362 * Record the pedigree of an attempt to return to a stolen parent
364 * The pedigree for an ORPHANED record is the pedigree of our parent
366 * @note It's assumed that replay_record_orphaned() has already checked that
367 * we're recording a log and that the record/replay functionality has not
368 * been compiled out.
370 * @param w The worker continuing noting that it has been orphaned.
372 void replay_record_orphaned_internal(__cilkrts_worker *w)
374 // Record steal: ORPHANED pedigree self
375 write_to_replay_log (w, PED_TYPE_STR_ORPHANED, w->pedigree.parent);
379 * Attempt to match a SYNC record. We have a match when this worker was
380 * recorded returning from the current call to __cilkrts_sync() with the
381 * same pedigree and this was the worker that continued from the sync, since
382 * it was the last to sync.
384 * If we find a match, the caller is expected to stall it is the last worker
385 * to reach a sync so it will be the worker to continue from the sync.
387 * @note It's assumed that replay_match_sync_pedigree() has already returned
388 * if we're not replaying a log, or if record/replay functionality has
389 * been compiled out.
391 * @param w The worker we're checking to see if we've got a match
393 int replay_match_sync_pedigree_internal(__cilkrts_worker *w)
395 // Return true if we have a match
396 if (w->l->replay_list_entry->match(ped_type_sync, &w->pedigree))
397 return 1;
398 else
399 return 0;
403 * Advance to the next log entry from a SYNC record. Consume the current
404 * SYNC record on this worker and advance to the next one.
406 * @note It's assumed that replay_advance_from_sync() has already returned if
407 * we're not replaying a log, or if record/replay functionality has been
408 * compiled out.
410 * @param w The worker whose replay log we're advancing.
412 void replay_advance_from_sync_internal (__cilkrts_worker *w)
414 // The current replay entry must be a SYNC
415 CILK_ASSERT(ped_type_sync == w->l->replay_list_entry->m_type);
417 // Advance to the next entry
418 w->l->replay_list_entry = w->l->replay_list_entry->next_entry();
422 * Called from random_steal() to override the ID of the randomly chosen victim
423 * worker which this worker will attempt to steal from. Returns the worker id
424 * of the next victim this worker was recorded stealing from, or -1 if the
425 * next record in the log is not a STEAL.
427 * @note This call does NOT attempt to match the pedigree. That will be done
428 * by replay_match_victim_pedigree() after random_steal() has locked the victim
429 * worker.
431 * @param w The __cilkrts_worker we're executing on. The worker's replay log
432 * is checked for a STEAL record. If we've got one, the stolen worker ID is
433 * returned.
435 * @return -1 if the next record is not a STEAL
436 * @return recorded stolen worker ID if we've got a matching STEAL record
438 int replay_get_next_recorded_victim_internal(__cilkrts_worker *w)
440 // If the next record isn't a STEAL, abort the attempt to steal work
441 if (ped_type_steal != w->l->replay_list_entry->m_type)
442 return -1;
444 // Return the victim's worker ID from the STEAL record. We'll check
445 // the pedigree after random_steal has locked the victim worker.
446 return w->l->replay_list_entry->m_value;
450 * Called from random_steal() to determine if we have a STEAL record that
451 * matches the pedigree at the head of the victim worker. If we do have a
452 * match, the STEAL record is consumed.
454 * @note It's assumed that replay_match_victim_pedigree() has already returned if
455 * we're not replaying a log, or if record/replay functionality has been
456 * compiled out.
458 * @return 1 if we have a match
459 * @return 0 if the current replay record isn't a STEAL record, or the victim
460 * isn't correct, or the pedigree doesn't match.
462 int replay_match_victim_pedigree_internal(__cilkrts_worker *w, __cilkrts_worker *victim)
464 // If we don't have a match, return 0
465 if (! w->l->replay_list_entry->match(ped_type_steal,
466 &((*victim->head)->parent_pedigree),
467 victim->self))
468 return 0;
470 // Consume this entry
471 w->l->replay_list_entry = w->l->replay_list_entry->next_entry();
473 // Return success
474 return 1;
478 * If the frame we're about to return to was recorded as being stolen,
479 * stall until it is.
481 * @note It's assumed that replay_wait_for_steal_if_parent_was_stolen() has
482 * already returned if we're not replaying a log, or if record/replay
483 * functionality has been compiled out.
485 * @param w The worker we're executing on.
487 void replay_wait_for_steal_if_parent_was_stolen_internal(__cilkrts_worker *w)
489 // If our parent wasn't recorded orphanen, return now
490 if (! w->l->replay_list_entry->match (ped_type_orphaned,
491 w->pedigree.parent))
492 return;
494 // Stall until our parent is stolen. Note that we're comparing head
495 // and tail, not head and exc. The steal is not completed until tail
496 // is modified.
497 while (!((w->tail - 1) < w->head))
498 __cilkrts_sleep();
500 // Consume the entry
501 w->l->replay_list_entry = w->l->replay_list_entry->next_entry();
505 * Allocate memory for the list of logged events.
507 * This function will read through the file and count the number of records
508 * so it can estimate how big a buffer to allocate for the array or replay
509 * entries. It will then rewind the file to the beginning so it can be
510 * loaded into memory.
512 * @param w The worker we're loading the file for.
513 * @param f The file of replay data we're scanning.
515 static
516 void allocate_replay_list(__cilkrts_worker *w, FILE *f)
518 // Count the number of entries - yeah, it's a hack, but it lets me
519 // allocate the space all at once instead of in chunks
520 char buf[1024];
521 int entries = 1; // Include "LAST" node
523 while (! feof(f))
525 if (fgets(buf, 1024, f))
527 // Skip the Workers record - should only be in file for Worker 0
528 if (0 != strncmp(PED_TYPE_STR_WORKERS, buf, sizeof(PED_TYPE_STR_WORKERS)-1))
529 entries++;
533 w->l->replay_list_root =
534 (replay_entry_t *)__cilkrts_malloc(entries * sizeof(replay_entry_t));
535 w->l->replay_list_root[entries - 1].m_type = ped_type_last;
537 // Reset the file to the beginning
538 rewind(f);
542 * Load the replay log for a worker into memory.
544 * @param w The worker we're loading the replay for.
546 static
547 void load_recorded_log(__cilkrts_worker *w)
549 char ped_type[PED_TYPE_SIZE];
550 char ped_str[PEDIGREE_BUFF_SIZE];
551 int32_t i1 = -1, i2 = -1;
552 int fret;
553 char local_replay_file_name[512];
554 FILE *f;
556 // Open the log for reading
557 cilk_snprintf_si(local_replay_file_name, sizeof(local_replay_file_name),
558 "%s%d.cilklog", w->g->record_replay_file_name, w->self);
560 f = fopen(local_replay_file_name, "r");
562 // Make sure we found a log!
563 CILK_ASSERT (NULL != f);
565 // Initialize the replay_list
566 allocate_replay_list(w, f);
567 replay_entry_t *entry = w->l->replay_list_root;
569 // Read the data out and add it to our tables
570 while (! feof(f))
572 #ifndef INCLUDE_SEQUENCE_NUMBER
573 fret = fscanf(f, "%s %s %d %d\n", ped_type, ped_str, &i1, &i2);
574 if(EOF == fret)
575 break;
577 // We must have read 4 fields
578 CILK_ASSERT(4 == fret);
579 #else
580 int32_t write_num;
581 fret = fscanf(f, "%s %s %d %d %d\n", ped_type, ped_str,
582 &i1, &i2, &write_num);
583 if(EOF == fret)
584 break;
586 // We must have read 5 fields
587 CILK_ASSERT(5 == fret);
588 #endif // INCLUDE_SEQUENCE_NUMBER
590 // Load the data into the entry
591 if (0 == strcmp(ped_type, PED_TYPE_STR_WORKERS))
593 // Verify we're replaying with the same number of workers we recorded with
594 if (i1 != w->g->P)
596 // Fatal error - does not return
597 cilkos_error("Cannot continue replay: number of workers(%d) doesn't match "
598 "that from the recording(%d).\n", w->g->P, i1);
601 // Verify that we understand this version of the pedigree file
602 if (PED_VERSION != i2)
604 // Fatal error - does not return
605 cilkos_error("Pedigree file version %d doesn't match current "
606 "version %d - cannot continue.\n",
607 i2, PED_VERSION);
610 else
612 entry->load(ped_type, ped_str, i1, i2);
613 entry++;
617 // Make sure we've filled the allocated memory. We initialized the last
618 // entry in
619 CILK_ASSERT(ped_type_last == entry->m_type);
620 w->l->replay_list_entry = w->l->replay_list_root;
622 // Close the log and return
623 fclose(f);
627 * Scan a recorded log to match STEALs againsted ORPHANED records.
629 * @param g Cilk Runtime global state. Passed to access the worker array so
630 * we can scan a worker's ORPHANED entries for one that matches a STEAL entry.
631 * @param entry The root of a replay_list for a worker.
633 static
634 void scan_for_matching_steals(global_state_t *g, replay_entry_t *entry)
636 // Iterate over all of the entries
637 while (ped_type_last != entry->m_type)
639 // Look for STEALs. That will tell us which worker the frame was
640 // stolen from
641 if (ped_type_steal == entry->m_type)
643 bool found = false;
645 // Validate the worker ID and make sure we've got a list
646 CILK_ASSERT((entry->m_value >= 0) && (entry->m_value < g->total_workers));
647 replay_entry_t *victim_entry = g->workers[entry->m_value]->l->replay_list_root;
648 CILK_ASSERT(NULL != victim_entry);
650 // Scan the victim's list for the matching ORPHANED record
651 while ((ped_type_last != victim_entry->m_type) && ! found)
653 if (ped_type_orphaned == victim_entry->m_type)
655 if (entry->m_pedigree_len == victim_entry->m_pedigree_len)
657 if (0 == memcmp(entry->m_reverse_pedigree,
658 victim_entry->m_reverse_pedigree,
659 entry->m_pedigree_len * sizeof(int64_t)))
661 // Note that this ORPHANED record has a matching steal
662 victim_entry->m_value = 0;
663 found = true;
667 victim_entry++;
670 entry++;
676 * Initialize per-worker data for record or replay - See record-replay.h
677 * for full routine header.
679 void replay_init_workers(global_state_t *g)
681 int i;
682 char worker_file_name[512];
684 // If we're not recording or replaying a log, we're done. All of the
685 // fields in the global_state_t or local_state_t are already initialized
686 // to default values.
687 if (RECORD_REPLAY_NONE == g->record_or_replay)
688 return;
690 // If we're replaying a log, read each worker's log and construct the
691 // in-memory log
692 if (REPLAY_LOG == g->record_or_replay)
694 // Read all of the data
695 for (i = 0; i < g->total_workers; ++i)
697 // This function will also initialize and fill the worker's
698 // replay list
699 load_recorded_log(g->workers[i]);
702 // Scan for orphans with no matching steal. Mark them so they'll be
703 // skipped as we advance through the log.
704 for (i = 0; i < g->total_workers; ++i)
706 scan_for_matching_steals(g, g->workers[i]->l->replay_list_root);
709 // If we're recording the logs while replaying, create the log files.
710 // This will only be used for debugging. Create the logs in the
711 // current directory. It should be as good a place as any...
712 #if RECORD_ON_REPLAY
713 for(i = 0; i < g->total_workers; ++i)
715 __cilkrts_worker *w = g->workers[i];
716 cilk_snprintf_i(worker_file_name, sizeof(worker_file_name),
717 "replay_log_%d.cilklog", w->self);
718 w->l->record_replay_fptr = fopen(worker_file_name, "w+");
719 CILK_ASSERT(NULL != w->l->record_replay_fptr);
722 // Record the number of workers, file version in Worker 0's file
723 write_to_replay_log (g->workers[0], PED_TYPE_STR_WORKERS, NULL, g->P, PED_VERSION);
724 #endif // RECORD_ON_REPLAY
727 // If we're recording, create the log files
728 if (RECORD_LOG == g->record_or_replay)
730 for(i = 0; i < g->total_workers; ++i)
732 __cilkrts_worker *w = g->workers[i];
733 cilk_snprintf_si(worker_file_name, sizeof(worker_file_name),
734 "%s%d.cilklog", g->record_replay_file_name, w->self);
735 w->l->record_replay_fptr = fopen(worker_file_name, "w+");
736 CILK_ASSERT(NULL != w->l->record_replay_fptr);
739 // Record the number of workers, file version in Worker 0's file
740 write_to_replay_log (g->workers[0], PED_TYPE_STR_WORKERS, NULL, g->P, PED_VERSION);
745 * Do any necessary cleanup for the logs - See record-replay.h for full
746 * routine header.
748 void replay_term(global_state_t *g)
750 // Free memory for the record/replay log file name, if we've got one
751 if (g->record_replay_file_name)
752 __cilkrts_free(g->record_replay_file_name);
754 // Per-worker cleanup
755 for(int i = 0; i < g->total_workers; ++i)
757 __cilkrts_worker *w = g->workers[i];
759 // Close the log files, if we've opened them
760 if(w->l->record_replay_fptr)
761 fclose(w->l->record_replay_fptr);
763 if (w->l->replay_list_root)
765 // We should have consumed the entire list
766 CILK_ASSERT(ped_type_last == w->l->replay_list_entry->m_type);
768 replay_entry_t *entry = w->l->replay_list_root;
769 while (ped_type_last != entry->m_type)
771 // Free the pedigree memory for each entry
772 entry->unload();
773 entry++;
775 __cilkrts_free(w->l->replay_list_root);
776 w->l->replay_list_root = NULL;
777 w->l->replay_list_entry = NULL;
782 __CILKRTS_END_EXTERN_C