1 /*===- InstrProfilingFile.c - Write instrumentation to a file -------------===*\ 2 |* 3 |* The LLVM Compiler Infrastructure 4 |* 5 |* This file is distributed under the University of Illinois Open Source 6 |* License. See LICENSE.TXT for details. 7 |* 8 \*===----------------------------------------------------------------------===*/ 9 10 #include <errno.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #ifdef _MSC_VER 15 /* For _alloca. */ 16 #include <malloc.h> 17 #endif 18 #if defined(_WIN32) 19 #include "WindowsMMap.h" 20 /* For _chsize_s */ 21 #include <io.h> 22 #include <process.h> 23 #else 24 #include <sys/file.h> 25 #include <sys/mman.h> 26 #include <unistd.h> 27 #if defined(__linux__) 28 #include <sys/types.h> 29 #endif 30 #endif 31 32 #include "InstrProfiling.h" 33 #include "InstrProfilingInternal.h" 34 #include "InstrProfilingUtil.h" 35 36 /* From where is profile name specified. 37 * The order the enumerators define their 38 * precedence. Re-order them may lead to 39 * runtime behavior change. */ 40 typedef enum ProfileNameSpecifier { 41 PNS_unknown = 0, 42 PNS_default, 43 PNS_command_line, 44 PNS_environment, 45 PNS_runtime_api 46 } ProfileNameSpecifier; 47 48 static const char *getPNSStr(ProfileNameSpecifier PNS) { 49 switch (PNS) { 50 case PNS_default: 51 return "default setting"; 52 case PNS_command_line: 53 return "command line"; 54 case PNS_environment: 55 return "environment variable"; 56 case PNS_runtime_api: 57 return "runtime API"; 58 default: 59 return "Unknown"; 60 } 61 } 62 63 #define MAX_PID_SIZE 16 64 /* Data structure holding the result of parsed filename pattern. */ 65 typedef struct lprofFilename { 66 /* File name string possibly with %p or %h specifiers. */ 67 const char *FilenamePat; 68 /* A flag indicating if FilenamePat's memory is allocated 69 * by runtime. */ 70 unsigned OwnsFilenamePat; 71 const char *ProfilePathPrefix; 72 char PidChars[MAX_PID_SIZE]; 73 char Hostname[COMPILER_RT_MAX_HOSTLEN]; 74 unsigned NumPids; 75 unsigned NumHosts; 76 /* When in-process merging is enabled, this parameter specifies 77 * the total number of profile data files shared by all the processes 78 * spawned from the same binary. By default the value is 1. If merging 79 * is not enabled, its value should be 0. This parameter is specified 80 * by the %[0-9]m specifier. For instance %2m enables merging using 81 * 2 profile data files. %1m is equivalent to %m. Also %m specifier 82 * can only appear once at the end of the name pattern. */ 83 unsigned MergePoolSize; 84 ProfileNameSpecifier PNS; 85 } lprofFilename; 86 87 COMPILER_RT_WEAK lprofFilename lprofCurFilename = {0, 0, 0, {0}, {0}, 88 0, 0, 0, PNS_unknown}; 89 90 static int getCurFilenameLength(); 91 static const char *getCurFilename(char *FilenameBuf); 92 static unsigned doMerging() { return lprofCurFilename.MergePoolSize; } 93 94 /* Return 1 if there is an error, otherwise return 0. */ 95 static uint32_t fileWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs, 96 uint32_t NumIOVecs) { 97 uint32_t I; 98 FILE *File = (FILE *)This->WriterCtx; 99 for (I = 0; I < NumIOVecs; I++) { 100 if (IOVecs[I].Data) { 101 if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) != 102 IOVecs[I].NumElm) 103 return 1; 104 } else { 105 if (fseek(File, IOVecs[I].ElmSize * IOVecs[I].NumElm, SEEK_CUR) == -1) 106 return 1; 107 } 108 } 109 return 0; 110 } 111 112 static void initFileWriter(ProfDataWriter *This, FILE *File) { 113 This->Write = fileWriter; 114 This->WriterCtx = File; 115 } 116 117 COMPILER_RT_VISIBILITY ProfBufferIO * 118 lprofCreateBufferIOInternal(void *File, uint32_t BufferSz) { 119 FreeHook = &free; 120 DynamicBufferIOBuffer = (uint8_t *)calloc(BufferSz, 1); 121 VPBufferSize = BufferSz; 122 ProfDataWriter *fileWriter = 123 (ProfDataWriter *)calloc(sizeof(ProfDataWriter), 1); 124 initFileWriter(fileWriter, File); 125 ProfBufferIO *IO = lprofCreateBufferIO(fileWriter); 126 IO->OwnFileWriter = 1; 127 return IO; 128 } 129 130 static void setupIOBuffer() { 131 const char *BufferSzStr = 0; 132 BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE"); 133 if (BufferSzStr && BufferSzStr[0]) { 134 VPBufferSize = atoi(BufferSzStr); 135 DynamicBufferIOBuffer = (uint8_t *)calloc(VPBufferSize, 1); 136 } 137 } 138 139 /* Read profile data in \c ProfileFile and merge with in-memory 140 profile counters. Returns -1 if there is fatal error, otheriwse 141 0 is returned. Returning 0 does not mean merge is actually 142 performed. If merge is actually done, *MergeDone is set to 1. 143 */ 144 static int doProfileMerging(FILE *ProfileFile, int *MergeDone) { 145 uint64_t ProfileFileSize; 146 char *ProfileBuffer; 147 148 if (fseek(ProfileFile, 0L, SEEK_END) == -1) { 149 PROF_ERR("Unable to merge profile data, unable to get size: %s\n", 150 strerror(errno)); 151 return -1; 152 } 153 ProfileFileSize = ftell(ProfileFile); 154 155 /* Restore file offset. */ 156 if (fseek(ProfileFile, 0L, SEEK_SET) == -1) { 157 PROF_ERR("Unable to merge profile data, unable to rewind: %s\n", 158 strerror(errno)); 159 return -1; 160 } 161 162 /* Nothing to merge. */ 163 if (ProfileFileSize < sizeof(__llvm_profile_header)) { 164 if (ProfileFileSize) 165 PROF_WARN("Unable to merge profile data: %s\n", 166 "source profile file is too small."); 167 return 0; 168 } 169 170 ProfileBuffer = mmap(NULL, ProfileFileSize, PROT_READ, MAP_SHARED | MAP_FILE, 171 fileno(ProfileFile), 0); 172 if (ProfileBuffer == MAP_FAILED) { 173 PROF_ERR("Unable to merge profile data, mmap failed: %s\n", 174 strerror(errno)); 175 return -1; 176 } 177 178 if (__llvm_profile_check_compatibility(ProfileBuffer, ProfileFileSize)) { 179 (void)munmap(ProfileBuffer, ProfileFileSize); 180 PROF_WARN("Unable to merge profile data: %s\n", 181 "source profile file is not compatible."); 182 return 0; 183 } 184 185 /* Now start merging */ 186 __llvm_profile_merge_from_buffer(ProfileBuffer, ProfileFileSize); 187 188 // Truncate the file in case merging of value profile did not happend to 189 // prevent from leaving garbage data at the end of the profile file. 190 COMPILER_RT_FTRUNCATE(ProfileFile, __llvm_profile_get_size_for_buffer()); 191 192 (void)munmap(ProfileBuffer, ProfileFileSize); 193 *MergeDone = 1; 194 195 return 0; 196 } 197 198 /* Create the directory holding the file, if needed. */ 199 static void createProfileDir(const char *Filename) { 200 size_t Length = strlen(Filename); 201 if (lprofFindFirstDirSeparator(Filename)) { 202 char *Copy = (char *)COMPILER_RT_ALLOCA(Length + 1); 203 strncpy(Copy, Filename, Length + 1); 204 __llvm_profile_recursive_mkdir(Copy); 205 } 206 } 207 208 /* Open the profile data for merging. It opens the file in r+b mode with 209 * file locking. If the file has content which is compatible with the 210 * current process, it also reads in the profile data in the file and merge 211 * it with in-memory counters. After the profile data is merged in memory, 212 * the original profile data is truncated and gets ready for the profile 213 * dumper. With profile merging enabled, each executable as well as any of 214 * its instrumented shared libraries dump profile data into their own data file. 215 */ 216 static FILE *openFileForMerging(const char *ProfileFileName, int *MergeDone) { 217 FILE *ProfileFile; 218 int rc; 219 220 createProfileDir(ProfileFileName); 221 ProfileFile = lprofOpenFileEx(ProfileFileName); 222 if (!ProfileFile) 223 return NULL; 224 225 rc = doProfileMerging(ProfileFile, MergeDone); 226 if (rc || (!*MergeDone && COMPILER_RT_FTRUNCATE(ProfileFile, 0L)) || 227 fseek(ProfileFile, 0L, SEEK_SET) == -1) { 228 PROF_ERR("Profile Merging of file %s failed: %s\n", ProfileFileName, 229 strerror(errno)); 230 fclose(ProfileFile); 231 return NULL; 232 } 233 return ProfileFile; 234 } 235 236 /* Write profile data to file \c OutputName. */ 237 static int writeFile(const char *OutputName) { 238 int RetVal; 239 FILE *OutputFile; 240 241 int MergeDone = 0; 242 VPMergeHook = &lprofMergeValueProfData; 243 if (!doMerging()) 244 OutputFile = fopen(OutputName, "ab"); 245 else 246 OutputFile = openFileForMerging(OutputName, &MergeDone); 247 248 if (!OutputFile) 249 return -1; 250 251 FreeHook = &free; 252 setupIOBuffer(); 253 ProfDataWriter fileWriter; 254 initFileWriter(&fileWriter, OutputFile); 255 RetVal = lprofWriteData(&fileWriter, lprofGetVPDataReader(), MergeDone); 256 257 fclose(OutputFile); 258 return RetVal; 259 } 260 261 static void truncateCurrentFile(void) { 262 const char *Filename; 263 char *FilenameBuf; 264 FILE *File; 265 int Length; 266 267 Length = getCurFilenameLength(); 268 FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1); 269 Filename = getCurFilename(FilenameBuf); 270 if (!Filename) 271 return; 272 273 /* By pass file truncation to allow online raw profile 274 * merging. */ 275 if (lprofCurFilename.MergePoolSize) 276 return; 277 278 createProfileDir(Filename); 279 280 /* Truncate the file. Later we'll reopen and append. */ 281 File = fopen(Filename, "w"); 282 if (!File) 283 return; 284 fclose(File); 285 } 286 287 static const char *DefaultProfileName = "default.profraw"; 288 static void resetFilenameToDefault(void) { 289 if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) { 290 free((void *)lprofCurFilename.FilenamePat); 291 } 292 memset(&lprofCurFilename, 0, sizeof(lprofCurFilename)); 293 lprofCurFilename.FilenamePat = DefaultProfileName; 294 lprofCurFilename.PNS = PNS_default; 295 } 296 297 static int containsMergeSpecifier(const char *FilenamePat, int I) { 298 return (FilenamePat[I] == 'm' || 299 (FilenamePat[I] >= '1' && FilenamePat[I] <= '9' && 300 /* If FilenamePat[I] is not '\0', the next byte is guaranteed 301 * to be in-bound as the string is null terminated. */ 302 FilenamePat[I + 1] == 'm')); 303 } 304 305 /* Parses the pattern string \p FilenamePat and stores the result to 306 * lprofcurFilename structure. */ 307 static int parseFilenamePattern(const char *FilenamePat, 308 unsigned CopyFilenamePat) { 309 int NumPids = 0, NumHosts = 0, I; 310 char *PidChars = &lprofCurFilename.PidChars[0]; 311 char *Hostname = &lprofCurFilename.Hostname[0]; 312 int MergingEnabled = 0; 313 314 /* Clean up cached prefix. */ 315 if (lprofCurFilename.ProfilePathPrefix) 316 free((void *)lprofCurFilename.ProfilePathPrefix); 317 memset(&lprofCurFilename, 0, sizeof(lprofCurFilename)); 318 319 if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) { 320 free((void *)lprofCurFilename.FilenamePat); 321 } 322 323 if (!CopyFilenamePat) 324 lprofCurFilename.FilenamePat = FilenamePat; 325 else { 326 lprofCurFilename.FilenamePat = strdup(FilenamePat); 327 lprofCurFilename.OwnsFilenamePat = 1; 328 } 329 /* Check the filename for "%p", which indicates a pid-substitution. */ 330 for (I = 0; FilenamePat[I]; ++I) 331 if (FilenamePat[I] == '%') { 332 if (FilenamePat[++I] == 'p') { 333 if (!NumPids++) { 334 if (snprintf(PidChars, MAX_PID_SIZE, "%ld", (long)getpid()) <= 0) { 335 PROF_WARN("Unable to get pid for filename pattern %s. Using the " 336 "default name.", 337 FilenamePat); 338 return -1; 339 } 340 } 341 } else if (FilenamePat[I] == 'h') { 342 if (!NumHosts++) 343 if (COMPILER_RT_GETHOSTNAME(Hostname, COMPILER_RT_MAX_HOSTLEN)) { 344 PROF_WARN("Unable to get hostname for filename pattern %s. Using " 345 "the default name.", 346 FilenamePat); 347 return -1; 348 } 349 } else if (containsMergeSpecifier(FilenamePat, I)) { 350 if (MergingEnabled) { 351 PROF_WARN("%%m specifier can only be specified once in %s.\n", 352 FilenamePat); 353 return -1; 354 } 355 MergingEnabled = 1; 356 if (FilenamePat[I] == 'm') 357 lprofCurFilename.MergePoolSize = 1; 358 else { 359 lprofCurFilename.MergePoolSize = FilenamePat[I] - '0'; 360 I++; /* advance to 'm' */ 361 } 362 } 363 } 364 365 lprofCurFilename.NumPids = NumPids; 366 lprofCurFilename.NumHosts = NumHosts; 367 return 0; 368 } 369 370 static void parseAndSetFilename(const char *FilenamePat, 371 ProfileNameSpecifier PNS, 372 unsigned CopyFilenamePat) { 373 374 const char *OldFilenamePat = lprofCurFilename.FilenamePat; 375 ProfileNameSpecifier OldPNS = lprofCurFilename.PNS; 376 377 if (PNS < OldPNS) 378 return; 379 380 if (!FilenamePat) 381 FilenamePat = DefaultProfileName; 382 383 if (OldFilenamePat && !strcmp(OldFilenamePat, FilenamePat)) { 384 lprofCurFilename.PNS = PNS; 385 return; 386 } 387 388 /* When PNS >= OldPNS, the last one wins. */ 389 if (!FilenamePat || parseFilenamePattern(FilenamePat, CopyFilenamePat)) 390 resetFilenameToDefault(); 391 lprofCurFilename.PNS = PNS; 392 393 if (!OldFilenamePat) { 394 if (getenv("LLVM_PROFILE_VERBOSE")) 395 PROF_NOTE("Set profile file path to \"%s\" via %s.\n", 396 lprofCurFilename.FilenamePat, getPNSStr(PNS)); 397 } else { 398 if (getenv("LLVM_PROFILE_VERBOSE")) 399 PROF_NOTE("Override old profile path \"%s\" via %s to \"%s\" via %s.\n", 400 OldFilenamePat, getPNSStr(OldPNS), lprofCurFilename.FilenamePat, 401 getPNSStr(PNS)); 402 } 403 404 truncateCurrentFile(); 405 } 406 407 /* Return buffer length that is required to store the current profile 408 * filename with PID and hostname substitutions. */ 409 /* The length to hold uint64_t followed by 2 digit pool id including '_' */ 410 #define SIGLEN 24 411 static int getCurFilenameLength() { 412 int Len; 413 if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0]) 414 return 0; 415 416 if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || 417 lprofCurFilename.MergePoolSize)) 418 return strlen(lprofCurFilename.FilenamePat); 419 420 Len = strlen(lprofCurFilename.FilenamePat) + 421 lprofCurFilename.NumPids * (strlen(lprofCurFilename.PidChars) - 2) + 422 lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2); 423 if (lprofCurFilename.MergePoolSize) 424 Len += SIGLEN; 425 return Len; 426 } 427 428 /* Return the pointer to the current profile file name (after substituting 429 * PIDs and Hostnames in filename pattern. \p FilenameBuf is the buffer 430 * to store the resulting filename. If no substitution is needed, the 431 * current filename pattern string is directly returned. */ 432 static const char *getCurFilename(char *FilenameBuf) { 433 int I, J, PidLength, HostNameLength; 434 const char *FilenamePat = lprofCurFilename.FilenamePat; 435 436 if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0]) 437 return 0; 438 439 if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || 440 lprofCurFilename.MergePoolSize)) 441 return lprofCurFilename.FilenamePat; 442 443 PidLength = strlen(lprofCurFilename.PidChars); 444 HostNameLength = strlen(lprofCurFilename.Hostname); 445 /* Construct the new filename. */ 446 for (I = 0, J = 0; FilenamePat[I]; ++I) 447 if (FilenamePat[I] == '%') { 448 if (FilenamePat[++I] == 'p') { 449 memcpy(FilenameBuf + J, lprofCurFilename.PidChars, PidLength); 450 J += PidLength; 451 } else if (FilenamePat[I] == 'h') { 452 memcpy(FilenameBuf + J, lprofCurFilename.Hostname, HostNameLength); 453 J += HostNameLength; 454 } else if (containsMergeSpecifier(FilenamePat, I)) { 455 char LoadModuleSignature[SIGLEN]; 456 int S; 457 int ProfilePoolId = getpid() % lprofCurFilename.MergePoolSize; 458 S = snprintf(LoadModuleSignature, SIGLEN, "%" PRIu64 "_%d", 459 lprofGetLoadModuleSignature(), ProfilePoolId); 460 if (S == -1 || S > SIGLEN) 461 S = SIGLEN; 462 memcpy(FilenameBuf + J, LoadModuleSignature, S); 463 J += S; 464 if (FilenamePat[I] != 'm') 465 I++; 466 } 467 /* Drop any unknown substitutions. */ 468 } else 469 FilenameBuf[J++] = FilenamePat[I]; 470 FilenameBuf[J] = 0; 471 472 return FilenameBuf; 473 } 474 475 /* Returns the pointer to the environment variable 476 * string. Returns null if the env var is not set. */ 477 static const char *getFilenamePatFromEnv(void) { 478 const char *Filename = getenv("LLVM_PROFILE_FILE"); 479 if (!Filename || !Filename[0]) 480 return 0; 481 return Filename; 482 } 483 484 COMPILER_RT_VISIBILITY 485 const char *__llvm_profile_get_path_prefix(void) { 486 int Length; 487 char *FilenameBuf, *Prefix; 488 const char *Filename, *PrefixEnd; 489 490 if (lprofCurFilename.ProfilePathPrefix) 491 return lprofCurFilename.ProfilePathPrefix; 492 493 Length = getCurFilenameLength(); 494 FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1); 495 Filename = getCurFilename(FilenameBuf); 496 if (!Filename) 497 return "\0"; 498 499 PrefixEnd = lprofFindLastDirSeparator(Filename); 500 if (!PrefixEnd) 501 return "\0"; 502 503 Length = PrefixEnd - Filename + 1; 504 Prefix = (char *)malloc(Length + 1); 505 if (!Prefix) { 506 PROF_ERR("Failed to %s\n", "allocate memory."); 507 return "\0"; 508 } 509 memcpy(Prefix, Filename, Length); 510 Prefix[Length] = '\0'; 511 lprofCurFilename.ProfilePathPrefix = Prefix; 512 return Prefix; 513 } 514 515 /* This method is invoked by the runtime initialization hook 516 * InstrProfilingRuntime.o if it is linked in. Both user specified 517 * profile path via -fprofile-instr-generate= and LLVM_PROFILE_FILE 518 * environment variable can override this default value. */ 519 COMPILER_RT_VISIBILITY 520 void __llvm_profile_initialize_file(void) { 521 const char *EnvFilenamePat; 522 const char *SelectedPat = NULL; 523 ProfileNameSpecifier PNS = PNS_unknown; 524 int hasCommandLineOverrider = (INSTR_PROF_PROFILE_NAME_VAR[0] != 0); 525 526 EnvFilenamePat = getFilenamePatFromEnv(); 527 if (EnvFilenamePat) { 528 /* Pass CopyFilenamePat = 1, to ensure that the filename would be valid 529 at the moment when __llvm_profile_write_file() gets executed. */ 530 parseAndSetFilename(EnvFilenamePat, PNS_environment, 1); 531 return; 532 } else if (hasCommandLineOverrider) { 533 SelectedPat = INSTR_PROF_PROFILE_NAME_VAR; 534 PNS = PNS_command_line; 535 } else { 536 SelectedPat = NULL; 537 PNS = PNS_default; 538 } 539 540 parseAndSetFilename(SelectedPat, PNS, 0); 541 } 542 543 /* This API is directly called by the user application code. It has the 544 * highest precedence compared with LLVM_PROFILE_FILE environment variable 545 * and command line option -fprofile-instr-generate=<profile_name>. 546 */ 547 COMPILER_RT_VISIBILITY 548 void __llvm_profile_set_filename(const char *FilenamePat) { 549 parseAndSetFilename(FilenamePat, PNS_runtime_api, 1); 550 } 551 552 /* The public API for writing profile data into the file with name 553 * set by previous calls to __llvm_profile_set_filename or 554 * __llvm_profile_override_default_filename or 555 * __llvm_profile_initialize_file. */ 556 COMPILER_RT_VISIBILITY 557 int __llvm_profile_write_file(void) { 558 int rc, Length; 559 const char *Filename; 560 char *FilenameBuf; 561 int PDeathSig = 0; 562 563 if (lprofProfileDumped()) { 564 PROF_NOTE("Profile data not written to file: %s.\n", 565 "already written"); 566 return 0; 567 } 568 569 Length = getCurFilenameLength(); 570 FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1); 571 Filename = getCurFilename(FilenameBuf); 572 573 /* Check the filename. */ 574 if (!Filename) { 575 PROF_ERR("Failed to write file : %s\n", "Filename not set"); 576 return -1; 577 } 578 579 /* Check if there is llvm/runtime version mismatch. */ 580 if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) { 581 PROF_ERR("Runtime and instrumentation version mismatch : " 582 "expected %d, but get %d\n", 583 INSTR_PROF_RAW_VERSION, 584 (int)GET_VERSION(__llvm_profile_get_version())); 585 return -1; 586 } 587 588 // Temporarily suspend getting SIGKILL when the parent exits. 589 PDeathSig = lprofSuspendSigKill(); 590 591 /* Write profile data to the file. */ 592 rc = writeFile(Filename); 593 if (rc) 594 PROF_ERR("Failed to write file \"%s\": %s\n", Filename, strerror(errno)); 595 596 // Restore SIGKILL. 597 if (PDeathSig == 1) 598 lprofRestoreSigKill(); 599 600 return rc; 601 } 602 603 COMPILER_RT_VISIBILITY 604 int __llvm_profile_dump(void) { 605 if (!doMerging()) 606 PROF_WARN("Later invocation of __llvm_profile_dump can lead to clobbering " 607 " of previously dumped profile data : %s. Either use %%m " 608 "in profile name or change profile name before dumping.\n", 609 "online profile merging is not on"); 610 int rc = __llvm_profile_write_file(); 611 lprofSetProfileDumped(); 612 return rc; 613 } 614 615 static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); } 616 617 COMPILER_RT_VISIBILITY 618 int __llvm_profile_register_write_file_atexit(void) { 619 static int HasBeenRegistered = 0; 620 621 if (HasBeenRegistered) 622 return 0; 623 624 lprofSetupValueProfiler(); 625 626 HasBeenRegistered = 1; 627 return atexit(writeFileWithoutReturn); 628 } 629