1/* record-replay.cpp -*-C++-*- 2 * 3 ************************************************************************* 4 * 5 * @copyright 6 * Copyright (C) 2012-2013, Intel Corporation 7 * All rights reserved. 8 * 9 * @copyright 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * * Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * * Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * * Neither the name of Intel Corporation nor the names of its 21 * contributors may be used to endorse or promote products derived 22 * from this software without specific prior written permission. 23 * 24 * @copyright 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 32 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 33 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 35 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 * 38 **************************************************************************/ 39 40/* 41 * Implementation of the record/replay functionality for Cilk Plus 42 */ 43 44#include <cstring> 45#include <vector> 46#include <stdlib.h> 47 48// clang is really strict about printf formats, so use the annoying integer 49// printf macros. Unfortunately they're not avaiable on Windows 50#ifdef _WIN32 51#define PRIu64 "llu" 52#else 53#define __STDC_FORMAT_MACROS 1 54#include <inttypes.h> 55#endif 56 57#include "record-replay.h" 58#include "bug.h" 59#include "internal/abi.h" 60#include "local_state.h" 61#include "full_frame.h" 62#include "global_state.h" 63#include "cilk_malloc.h" 64#include "os.h" // for cilkos_error() 65 66#if RECORD_ON_REPLAY 67#pragma message ("*** Record on Replay is enabled!") 68#endif 69 70// Defined to write sequence number to the logs. Note that you cannot 71// diff logs with sequence numbers because the numbers may increment in 72// different orders. 73//#define INCLUDE_SEQUENCE_NUMBER 1 74 75const int PED_VERSION = 1; // Log recording version 76 77// Log types 78enum ped_type_t 79{ 80 ped_type_unknown, 81 ped_type_steal, 82 ped_type_sync, 83 ped_type_orphaned, 84 ped_type_last // Flags end of the list 85}; 86 87// Log type strings 88#define PED_TYPE_STR_STEAL "Steal" 89#define PED_TYPE_STR_SYNC "Sync" 90#define PED_TYPE_STR_WORKERS "Workers" 91#define PED_TYPE_STR_ORPHANED "Orphaned" 92 93#define PED_TYPE_SIZE 16 // Buffer size for the type of pedigree. Must 94 // hold largest pedigree record type string. 95#define PEDIGREE_BUFF_SIZE 512 // Buffer size for the string representation 96 // of a pedigree. 97 98/** 99 * Data we store for a replay log entry 100 */ 101typedef struct replay_entry_t 102{ 103 uint64_t *m_reverse_pedigree; /**< Reverse pedigree for replay log entry */ 104 ped_type_t m_type; /**< Type of replay log entry */ 105 int16_t m_pedigree_len; /**< Number of terms in reverse pedigree */ 106 int16_t m_value; /**< Victim for STEALs, 0 if matching steal found for ORPHANs */ 107 108 /** 109 * Load data read from the log into the entry 110 */ 111 bool load(const char *type, const char *pedigee_str, int32_t value1, int32_t value2) 112 { 113 // Convert the type into an enum 114 if (0 == strcmp(type, PED_TYPE_STR_STEAL)) 115 { 116 m_type = ped_type_steal; 117 m_value = (int16_t)value1; // Victim 118 } 119 else 120 { 121 m_value = -1; // Victim not valid 122 if (0 == strcmp(type, PED_TYPE_STR_SYNC)) 123 m_type = ped_type_sync; 124 else if (0 == strcmp(type, PED_TYPE_STR_ORPHANED)) 125 m_type = ped_type_orphaned; 126 else 127 { 128 m_type = ped_type_unknown; 129 return false; 130 } 131 } 132 133 // Parse the pedigree 134 m_pedigree_len = 0; 135 136 const char *p = pedigee_str; 137 char *end; 138 139 uint64_t temp_pedigree[PEDIGREE_BUFF_SIZE/2]; 140 141 while(1) 142 { 143 temp_pedigree[m_pedigree_len++] = (uint64_t)strtol(p, &end, 10); 144 if ('\0' == *end) 145 break; 146 p = end + 1; 147 } 148 149 // Allocate memory to hold the pedigree. 150 // Copy the pedigree in reverse order since that's the order we'll 151 // traverse it 152 m_reverse_pedigree = 153 (uint64_t *)__cilkrts_malloc(sizeof(int64_t) * m_pedigree_len); 154 for (int n = 0; n < m_pedigree_len; n++) 155 m_reverse_pedigree[n] = temp_pedigree[(m_pedigree_len - 1) - n]; 156 157 return true; 158 } 159 160 /** 161 * Match this entry against the data supplied. This includes walking the 162 * pedigree from the specified node. 163 */ 164 bool match (ped_type_t type, const __cilkrts_pedigree *node, int victim = -1) 165 { 166 int i = 0; 167 168 // If the type isn't what they're seeking, we don't have a match 169 if (type != m_type) 170 return false; 171 172 // If we're looking for a STEAL, then the victim must match 173 if ((type == ped_type_steal) && (victim != m_value)) 174 return false; 175 176 // Compare the current pedigree against what was recorded 177 while ((NULL != node) && (i < m_pedigree_len)) 178 { 179 // If we've got a pedigree rank difference, then we don't have 180 // a match 181 if (node->rank != m_reverse_pedigree[i]) 182 return false; 183 node = node->parent; 184 i++; 185 } 186 187 // Make sure we exhausted both the pedigree chain and the recorded 188 // pedigree 189 return ((NULL == node) && (i == m_pedigree_len)); 190 } 191 192 /** 193 * Advance to the next entry, skipping any ORPHANED records we didn't see 194 * a matching STEAL for 195 */ 196 replay_entry_t *next_entry() 197 { 198 replay_entry_t *entry = this; 199 200 // You can't go beyond the end 201 if (ped_type_last == entry->m_type) 202 return entry; 203 204 // Advance to the next entry 205 entry++; 206 207 // Skip any ORPHANED records that don't have a matching steal. We 208 // initialized the value field to -1 for ORPHANED. After loading all 209 // the log data, we iterated through all the STEAL records setting the 210 // matching ORPHANED record's value field to 0. So if an ORPHANED 211 // record's value field is still -1, it doesn't have a matching STEAL 212 // record, and I don't know why we chose not to return from the 213 // spawned function. 214 while ((ped_type_orphaned == entry->m_type) && (-1 == entry->m_value)) 215 { 216 entry++; 217 } 218 219 return entry; 220 } 221 222 /** 223 * Release any allocated resources 224 */ 225 void unload() 226 { 227 __cilkrts_free(m_reverse_pedigree); 228 m_reverse_pedigree = NULL; 229 } 230 231} replay_entry_t; 232 233__CILKRTS_BEGIN_EXTERN_C 234 235/** 236 * Walk the pedigree and generate a string representation with underscores 237 * between terms. Currently does a recursive walk to generate a forward 238 * pedigree. 239 * 240 * @param p The buffer that is to be filled. Assumed to be PEDIGREE_BUFF_SIZE 241 * characters long 242 * @param pnode The initial pedigree term to be written. 243 * 244 * @return A pointer into the pedigree string buffer after a term has been 245 * written. 246 */ 247static 248char * walk_pedigree_nodes(char *p, const __cilkrts_pedigree *pnode) 249{ 250 CILK_ASSERT(pnode); 251 if (pnode->parent) 252 { 253 p = walk_pedigree_nodes(p, pnode->parent); 254 p += sprintf(p, "_"); 255 } 256 257 return p + sprintf(p, "%" PRIu64, pnode->rank); 258} 259 260/** 261 * Write a record to a replay log file. 262 * 263 * @param w The worker we're writing the pedigree for. 264 * @param type The type of the pedigree record, as a string 265 * @param initial_node The initial pedigree node to be written, or NULL if 266 * there is no pedigree for this record type. 267 * @param i1 First integer value to be written to the record. 268 * @param i2 Second integer value to be written to the record. Only applies 269 * to STEAL records. Defaults to -1 (unused). The second value is always 270 * written to make parsing easier. 271 */ 272static 273void write_to_replay_log (__cilkrts_worker *w, const char *type, 274 const __cilkrts_pedigree *initial_node, 275 int i1 = -1, int i2 = -1) 276{ 277 char pedigree[PEDIGREE_BUFF_SIZE]; 278 279 // If we don't have an initial pedigree node, just use "0" to fill the slot 280 if (NULL == initial_node) 281 strcpy(pedigree, "0"); 282 else 283 walk_pedigree_nodes(pedigree, initial_node); 284 285#ifndef INCLUDE_SEQUENCE_NUMBER 286 // Simply write the record 287 fprintf(w->l->record_replay_fptr, "%s %s %d %d\n", 288 type, pedigree, i1, i2); 289#else 290 // Write the record with a sequence number. The sequence number should 291 // always be the last term, and ignored on read 292 293 static long volatile seq_num = 0; 294 long write_num; 295 296 // Atomic increment functions are compiler/OS-specific 297#ifdef _WIN32 298 write_num = _InterlockedIncrement(&seq_num); 299#else /* GCC */ 300 write_num = __sync_add_and_fetch(&seq_num, 1); 301#endif // _WIN32 302 303 fprintf(w->l->record_replay_fptr, "%s %s %d %d %ld\n", 304 type, pedigree, i1, i2, write_num); 305#endif // INCLUDE_SEQUENCE_NUMBER 306 307 fflush(w->l->record_replay_fptr); 308} 309 310/** 311 * Record data for a successful steal. 312 * 313 * The pedigree for a STEAL record is the pedigree of the stolen frame. 314 * 315 * @note It's assumed that replay_record_steal() has already checked that we're 316 * recording a log and that the record/replay functionality has not been 317 * compiled out. 318 * 319 * @param w The worker stealing a frame. 320 * @param victim_id The ID of the worker which had it's frame stolen. 321 */ 322void replay_record_steal_internal(__cilkrts_worker *w, int32_t victim_id) 323{ 324 // Follow the pedigree chain using worker's stack frame 325 CILK_ASSERT(w->l->next_frame_ff); 326 CILK_ASSERT(w->l->next_frame_ff->call_stack); 327 328 // Record steal: STEAL pedigree victim_id thief_id 329 write_to_replay_log (w, PED_TYPE_STR_STEAL, 330 &(w->l->next_frame_ff->call_stack->parent_pedigree), 331 victim_id); 332} 333 334/** 335 * Record data for the worker that continues from a sync 336 * 337 * The pedigree for a SYNC record is the pedigree at the sync. 338 * 339 * @note It's assumed that replay_record_sync() has already checked that we're 340 * recording a log and that the record/replay functionality has not been 341 * compiled out. 342 * 343 * @param w The worker continuing from a sync. 344 */ 345void replay_record_sync_internal(__cilkrts_worker *w) 346{ 347 // Record sync: SYNC pedigree last_worker_id 348 write_to_replay_log (w, PED_TYPE_STR_SYNC, &w->pedigree); 349} 350 351/** 352 * Record the pedigree of an attempt to return to a stolen parent 353 * 354 * The pedigree for an ORPHANED record is the pedigree of our parent 355 * 356 * @note It's assumed that replay_record_orphaned() has already checked that 357 * we're recording a log and that the record/replay functionality has not 358 * been compiled out. 359 * 360 * @param w The worker continuing noting that it has been orphaned. 361 */ 362void replay_record_orphaned_internal(__cilkrts_worker *w) 363{ 364 // Record steal: ORPHANED pedigree self 365 write_to_replay_log (w, PED_TYPE_STR_ORPHANED, w->pedigree.parent); 366} 367 368/** 369 * Attempt to match a SYNC record. We have a match when this worker was 370 * recorded returning from the current call to __cilkrts_sync() with the 371 * same pedigree and this was the worker that continued from the sync, since 372 * it was the last to sync. 373 * 374 * If we find a match, the caller is expected to stall it is the last worker 375 * to reach a sync so it will be the worker to continue from the sync. 376 * 377 * @note It's assumed that replay_match_sync_pedigree() has already returned 378 * if we're not replaying a log, or if record/replay functionality has 379 * been compiled out. 380 * 381 * @param w The worker we're checking to see if we've got a match 382 */ 383int replay_match_sync_pedigree_internal(__cilkrts_worker *w) 384{ 385 // Return true if we have a match 386 if (w->l->replay_list_entry->match(ped_type_sync, &w->pedigree)) 387 return 1; 388 else 389 return 0; 390} 391 392/** 393 * Advance to the next log entry from a SYNC record. Consume the current 394 * SYNC record on this worker and advance to the next one. 395 * 396 * @note It's assumed that replay_advance_from_sync() has already returned if 397 * we're not replaying a log, or if record/replay functionality has been 398 * compiled out. 399 * 400 * @param w The worker whose replay log we're advancing. 401 */ 402void replay_advance_from_sync_internal (__cilkrts_worker *w) 403{ 404 // The current replay entry must be a SYNC 405 CILK_ASSERT(ped_type_sync == w->l->replay_list_entry->m_type); 406 407 // Advance to the next entry 408 w->l->replay_list_entry = w->l->replay_list_entry->next_entry(); 409} 410 411/** 412 * Called from random_steal() to override the ID of the randomly chosen victim 413 * worker which this worker will attempt to steal from. Returns the worker id 414 * of the next victim this worker was recorded stealing from, or -1 if the 415 * next record in the log is not a STEAL. 416 * 417 * @note This call does NOT attempt to match the pedigree. That will be done 418 * by replay_match_victim_pedigree() after random_steal() has locked the victim 419 * worker. 420 * 421 * @param w The __cilkrts_worker we're executing on. The worker's replay log 422 * is checked for a STEAL record. If we've got one, the stolen worker ID is 423 * returned. 424 * 425 * @return -1 if the next record is not a STEAL 426 * @return recorded stolen worker ID if we've got a matching STEAL record 427 */ 428int replay_get_next_recorded_victim_internal(__cilkrts_worker *w) 429{ 430 // If the next record isn't a STEAL, abort the attempt to steal work 431 if (ped_type_steal != w->l->replay_list_entry->m_type) 432 return -1; 433 434 // Return the victim's worker ID from the STEAL record. We'll check 435 // the pedigree after random_steal has locked the victim worker. 436 return w->l->replay_list_entry->m_value; 437} 438 439/** 440 * Called from random_steal() to determine if we have a STEAL record that 441 * matches the pedigree at the head of the victim worker. If we do have a 442 * match, the STEAL record is consumed. 443 * 444 * @note It's assumed that replay_match_victim_pedigree() has already returned if 445 * we're not replaying a log, or if record/replay functionality has been 446 * compiled out. 447 * 448 * @return 1 if we have a match 449 * @return 0 if the current replay record isn't a STEAL record, or the victim 450 * isn't correct, or the pedigree doesn't match. 451 */ 452int replay_match_victim_pedigree_internal(__cilkrts_worker *w, __cilkrts_worker *victim) 453{ 454 // If we don't have a match, return 0 455 if (! w->l->replay_list_entry->match(ped_type_steal, 456 &((*victim->head)->parent_pedigree), 457 victim->self)) 458 return 0; 459 460 // Consume this entry 461 w->l->replay_list_entry = w->l->replay_list_entry->next_entry(); 462 463 // Return success 464 return 1; 465} 466 467/** 468 * If the frame we're about to return to was recorded as being stolen, 469 * stall until it is. 470 * 471 * @note It's assumed that replay_wait_for_steal_if_parent_was_stolen() has 472 * already returned if we're not replaying a log, or if record/replay 473 * functionality has been compiled out. 474 * 475 * @param w The worker we're executing on. 476 */ 477void replay_wait_for_steal_if_parent_was_stolen_internal(__cilkrts_worker *w) 478{ 479 // If our parent wasn't recorded orphanen, return now 480 if (! w->l->replay_list_entry->match (ped_type_orphaned, 481 w->pedigree.parent)) 482 return; 483 484 // Stall until our parent is stolen. Note that we're comparing head 485 // and tail, not head and exc. The steal is not completed until tail 486 // is modified. 487 while (!((w->tail - 1) < w->head)) 488 __cilkrts_sleep(); 489 490 // Consume the entry 491 w->l->replay_list_entry = w->l->replay_list_entry->next_entry(); 492} 493 494/** 495 * Allocate memory for the list of logged events. 496 * 497 * This function will read through the file and count the number of records 498 * so it can estimate how big a buffer to allocate for the array or replay 499 * entries. It will then rewind the file to the beginning so it can be 500 * loaded into memory. 501 * 502 * @param w The worker we're loading the file for. 503 * @param f The file of replay data we're scanning. 504 */ 505static 506void allocate_replay_list(__cilkrts_worker *w, FILE *f) 507{ 508 // Count the number of entries - yeah, it's a hack, but it lets me 509 // allocate the space all at once instead of in chunks 510 char buf[1024]; 511 int entries = 1; // Include "LAST" node 512 513 while (! feof(f)) 514 { 515 if (fgets(buf, 1024, f)) 516 { 517 // Skip the Workers record - should only be in file for Worker 0 518 if (0 != strncmp(PED_TYPE_STR_WORKERS, buf, sizeof(PED_TYPE_STR_WORKERS)-1)) 519 entries++; 520 } 521 } 522 523 w->l->replay_list_root = 524 (replay_entry_t *)__cilkrts_malloc(entries * sizeof(replay_entry_t)); 525 w->l->replay_list_root[entries - 1].m_type = ped_type_last; 526 527 // Reset the file to the beginning 528 rewind(f); 529} 530 531/** 532 * Load the replay log for a worker into memory. 533 * 534 * @param w The worker we're loading the replay for. 535 */ 536static 537void load_recorded_log(__cilkrts_worker *w) 538{ 539 char ped_type[PED_TYPE_SIZE]; 540 char ped_str[PEDIGREE_BUFF_SIZE]; 541 int32_t i1 = -1, i2 = -1; 542 int fret; 543 char local_replay_file_name[512]; 544 FILE *f; 545 546 // Open the log for reading 547 sprintf(local_replay_file_name, "%s%d.cilklog", w->g->record_replay_file_name, w->self); 548 f = fopen(local_replay_file_name, "r"); 549 550 // Make sure we found a log! 551 CILK_ASSERT (NULL != f); 552 553 // Initialize the replay_list 554 allocate_replay_list(w, f); 555 replay_entry_t *entry = w->l->replay_list_root; 556 557 // Read the data out and add it to our tables 558 while (! feof(f)) 559 { 560#ifndef INCLUDE_SEQUENCE_NUMBER 561 fret = fscanf(f, "%s %s %d %d\n", ped_type, ped_str, &i1, &i2); 562 if(EOF == fret) 563 break; 564 565 // We must have read 4 fields 566 CILK_ASSERT(4 == fret); 567#else 568 int32_t write_num; 569 fret = fscanf(f, "%s %s %d %d %d\n", ped_type, ped_str, 570 &i1, &i2, &write_num); 571 if(EOF == fret) 572 break; 573 574 // We must have read 5 fields 575 CILK_ASSERT(5 == fret); 576#endif // INCLUDE_SEQUENCE_NUMBER 577 578 // Load the data into the entry 579 if (0 == strcmp(ped_type, PED_TYPE_STR_WORKERS)) 580 { 581 // Verify we're replaying with the same number of workers we recorded with 582 if (i1 != w->g->P) 583 { 584 // Fatal error - does not return 585 cilkos_error("Cannot continue replay: number of workers(%d) doesn't match " 586 "that from the recording(%d).\n", w->g->P, i1); 587 } 588 589 // Verify that we understand this version of the pedigree file 590 if (PED_VERSION != i2) 591 { 592 // Fatal error - does not return 593 cilkos_error("Pedigree file version %d doesn't match current " 594 "version %d - cannot continue.\n", 595 i2, PED_VERSION); 596 } 597 } 598 else 599 { 600 entry->load(ped_type, ped_str, i1, i2); 601 entry++; 602 } 603 } 604 605 // Make sure we've filled the allocated memory. We initialized the last 606 // entry in 607 CILK_ASSERT(ped_type_last == entry->m_type); 608 w->l->replay_list_entry = w->l->replay_list_root; 609 610 // Close the log and return 611 fclose(f); 612} 613 614/** 615 * Scan a recorded log to match STEALs againsted ORPHANED records. 616 * 617 * @param g Cilk Runtime global state. Passed to access the worker array so 618 * we can scan a worker's ORPHANED entries for one that matches a STEAL entry. 619 * @param entry The root of a replay_list for a worker. 620 */ 621static 622void scan_for_matching_steals(global_state_t *g, replay_entry_t *entry) 623{ 624 // Iterate over all of the entries 625 while (ped_type_last != entry->m_type) 626 { 627 // Look for STEALs. That will tell us which worker the frame was 628 // stolen from 629 if (ped_type_steal == entry->m_type) 630 { 631 bool found = false; 632 633 // Validate the worker ID and make sure we've got a list 634 CILK_ASSERT((entry->m_value >= 0) && (entry->m_value < g->total_workers)); 635 replay_entry_t *victim_entry = g->workers[entry->m_value]->l->replay_list_root; 636 CILK_ASSERT(NULL != victim_entry); 637 638 // Scan the victim's list for the matching ORPHANED record 639 while ((ped_type_last != victim_entry->m_type) && ! found) 640 { 641 if (ped_type_orphaned == victim_entry->m_type) 642 { 643 if (entry->m_pedigree_len == victim_entry->m_pedigree_len) 644 { 645 if (0 == memcmp(entry->m_reverse_pedigree, 646 victim_entry->m_reverse_pedigree, 647 entry->m_pedigree_len * sizeof(int64_t))) 648 { 649 // Note that this ORPHANED record has a matching steal 650 victim_entry->m_value = 0; 651 found = true; 652 } 653 } 654 } 655 victim_entry++; 656 } 657 } 658 entry++; 659 } 660} 661 662 663/* 664 * Initialize per-worker data for record or replay - See record-replay.h 665 * for full routine header. 666 */ 667void replay_init_workers(global_state_t *g) 668{ 669 int i; 670 char worker_file_name[512]; 671 672 // If we're not recording or replaying a log, we're done. All of the 673 // fields in the global_state_t or local_state_t are already initialized 674 // to default values. 675 if (RECORD_REPLAY_NONE == g->record_or_replay) 676 return; 677 678 // If we're replaying a log, read each worker's log and construct the 679 // in-memory log 680 if (REPLAY_LOG == g->record_or_replay) 681 { 682 // Read all of the data 683 for (i = 0; i < g->total_workers; ++i) 684 { 685 // This function will also initialize and fill the worker's 686 // replay list 687 load_recorded_log(g->workers[i]); 688 } 689 690 // Scan for orphans with no matching steal. Mark them so they'll be 691 // skipped as we advance through the log. 692 for (i = 0; i < g->total_workers; ++i) 693 { 694 scan_for_matching_steals(g, g->workers[i]->l->replay_list_root); 695 } 696 697 // If we're recording the logs while replaying, create the log files. 698 // This will only be used for debugging. Create the logs in the 699 // current directory. It should be as good a place as any... 700#if RECORD_ON_REPLAY 701 for(i = 0; i < g->total_workers; ++i) 702 { 703 __cilkrts_worker *w = g->workers[i]; 704 sprintf(worker_file_name, "replay_log_%d.cilklog", w->self); 705 w->l->record_replay_fptr = fopen(worker_file_name, "w+"); 706 CILK_ASSERT(NULL != w->l->record_replay_fptr); 707 } 708 709 // Record the number of workers, file version in Worker 0's file 710 write_to_replay_log (g->workers[0], PED_TYPE_STR_WORKERS, NULL, g->P, PED_VERSION); 711#endif // RECORD_ON_REPLAY 712 } 713 714 // If we're recording, create the log files 715 if (RECORD_LOG == g->record_or_replay) 716 { 717 for(i = 0; i < g->total_workers; ++i) 718 { 719 __cilkrts_worker *w = g->workers[i]; 720 sprintf(worker_file_name, "%s%d.cilklog", 721 g->record_replay_file_name, 722 w->self); 723 w->l->record_replay_fptr = fopen(worker_file_name, "w+"); 724 CILK_ASSERT(NULL != w->l->record_replay_fptr); 725 } 726 727 // Record the number of workers, file version in Worker 0's file 728 write_to_replay_log (g->workers[0], PED_TYPE_STR_WORKERS, NULL, g->P, PED_VERSION); 729 } 730} 731 732/* 733 * Do any necessary cleanup for the logs - See record-replay.h for full 734 * routine header. 735 */ 736void replay_term(global_state_t *g) 737{ 738 // Free memory for the record/replay log file name, if we've got one 739 if (g->record_replay_file_name) 740 __cilkrts_free(g->record_replay_file_name); 741 742 // Per-worker cleanup 743 for(int i = 0; i < g->total_workers; ++i) 744 { 745 __cilkrts_worker *w = g->workers[i]; 746 747 // Close the log files, if we've opened them 748 if(w->l->record_replay_fptr) 749 fclose(w->l->record_replay_fptr); 750 751 if (w->l->replay_list_root) 752 { 753 // We should have consumed the entire list 754 CILK_ASSERT(ped_type_last == w->l->replay_list_entry->m_type); 755 756 replay_entry_t *entry = w->l->replay_list_root; 757 while (ped_type_last != entry->m_type) 758 { 759 // Free the pedigree memory for each entry 760 entry->unload(); 761 entry++; 762 } 763 __cilkrts_free(w->l->replay_list_root); 764 w->l->replay_list_root = NULL; 765 w->l->replay_list_entry = NULL; 766 } 767 } 768} 769 770__CILKRTS_END_EXTERN_C 771