// Do a three step traversal: by file, then fn, then line. // In all cases prepends new nodes to their chain. Returns a pointer to the // line node, creates a new one if necessary. static lineCC* get_lineCC(Addr origAddr) { fileCC *curr_fileCC; fnCC *curr_fnCC; lineCC *curr_lineCC; Char file[FILE_LEN], fn[FN_LEN]; Int line; UInt file_hash, fn_hash, line_hash; get_debug_info(origAddr, file, fn, &line); VGP_PUSHCC(VgpGetLineCC); // level 1 file_hash = hash(file, N_FILE_ENTRIES); curr_fileCC = CC_table[file_hash]; while (NULL != curr_fileCC && !VG_STREQ(file, curr_fileCC->file)) { curr_fileCC = curr_fileCC->next; } if (NULL == curr_fileCC) { CC_table[file_hash] = curr_fileCC = new_fileCC(file, CC_table[file_hash]); distinct_files++; } // level 2 fn_hash = hash(fn, N_FN_ENTRIES); curr_fnCC = curr_fileCC->fns[fn_hash]; while (NULL != curr_fnCC && !VG_STREQ(fn, curr_fnCC->fn)) { curr_fnCC = curr_fnCC->next; } if (NULL == curr_fnCC) { curr_fileCC->fns[fn_hash] = curr_fnCC = new_fnCC(fn, curr_fileCC->fns[fn_hash]); distinct_fns++; } // level 3 line_hash = line % N_LINE_ENTRIES; curr_lineCC = curr_fnCC->lines[line_hash]; while (NULL != curr_lineCC && line != curr_lineCC->line) { curr_lineCC = curr_lineCC->next; } if (NULL == curr_lineCC) { curr_fnCC->lines[line_hash] = curr_lineCC = new_lineCC(line, curr_fnCC->lines[line_hash]); distinct_lines++; } VGP_POPCC(VgpGetLineCC); return curr_lineCC; }
Bool SK_(error_matches_suppression)(Error* err, Supp* su) { Int su_size; MAC_Error* err_extra = VG_(get_error_extra)(err); ErrorKind ekind = VG_(get_error_kind )(err); switch (VG_(get_supp_kind)(su)) { case ParamSupp: return (ekind == ParamErr && VG_STREQ(VG_(get_error_string)(err), VG_(get_supp_string)(su))); case CoreMemSupp: return (ekind == CoreMemErr && VG_STREQ(VG_(get_error_string)(err), VG_(get_supp_string)(su))); case Value0Supp: su_size = 0; goto value_case; case Value1Supp: su_size = 1; goto value_case; case Value2Supp: su_size = 2; goto value_case; case Value4Supp: su_size = 4; goto value_case; case Value8Supp: su_size = 8; goto value_case; case Value16Supp:su_size =16; goto value_case; value_case: return (ekind == ValueErr && err_extra->size == su_size); case Addr1Supp: su_size = 1; goto addr_case; case Addr2Supp: su_size = 2; goto addr_case; case Addr4Supp: su_size = 4; goto addr_case; case Addr8Supp: su_size = 8; goto addr_case; case Addr16Supp:su_size =16; goto addr_case; addr_case: return (ekind == AddrErr && err_extra->size == su_size); case FreeSupp: return (ekind == FreeErr || ekind == FreeMismatchErr); case OverlapSupp: return (ekind = OverlapErr); case LeakSupp: return (ekind == LeakErr); default: VG_(printf)("Error:\n" " unknown suppression type %d\n", VG_(get_supp_kind)(su)); VG_(skin_panic)("unknown suppression type in " "SK_(error_matches_suppression)"); } }
Bool pc_is_recognised_suppression ( Char* name, Supp *su ) { SuppKind skind; if (VG_STREQ(name, "SorG")) skind = XS_SorG; else if (VG_STREQ(name, "Heap")) skind = XS_Heap; else if (VG_STREQ(name, "Arith")) skind = XS_Arith; else if (VG_STREQ(name, "SysParam")) skind = XS_SysParam; else return False; VG_(set_supp_kind)(su, skind); return True; }
static Bool match_executable(const HChar *entry) { HChar buf[VG_(strlen)(entry) + VG_(strlen)(executable_name_in) + 3]; /* empty PATH element means '.' */ if (*entry == '\0') entry = "."; VG_(snprintf)(buf, sizeof(buf), "%s/%s", entry, executable_name_in); // Don't match directories if (VG_(is_dir)(buf)) return False; // If we match an executable, we choose that immediately. If we find a // matching non-executable we remember it but keep looking for an // matching executable later in the path. if (VG_(access)(buf, True/*r*/, False/*w*/, True/*x*/) == 0) { VG_(strncpy)( executable_name_out, buf, VKI_PATH_MAX-1 ); executable_name_out[VKI_PATH_MAX-1] = 0; return True; // Stop looking } else if (VG_(access)(buf, True/*r*/, False/*w*/, False/*x*/) == 0 && VG_STREQ(executable_name_out, "")) { VG_(strncpy)( executable_name_out, buf, VKI_PATH_MAX-1 ); executable_name_out[VKI_PATH_MAX-1] = 0; return False; // Keep looking } else { return False; // Keep looking } }
void VG_(assert_fail) ( Bool isCore, const HChar* expr, const HChar* file, Int line, const HChar* fn, const HChar* format, ... ) { va_list vargs; HChar buf[512]; const HChar* component; const HChar* bugs_to; UInt written; static Bool entered = False; if (entered) VG_(exit)(2); entered = True; va_start(vargs, format); written = VG_(vsnprintf) ( buf, sizeof(buf), format, vargs ); va_end(vargs); if (written >= sizeof(buf)) { VG_(printf)("\nvalgrind: %s: buf is too small, sizeof(buf) = %u, " "written = %d\n", __func__, (unsigned)sizeof(buf), written); } if (isCore) { component = "valgrind"; bugs_to = VG_BUGS_TO; } else { component = VG_(details).name; bugs_to = VG_(details).bug_reports_to; } if (VG_(clo_xml)) VG_(printf_xml)("</valgrindoutput>\n"); // Treat vg_assert2(0, "foo") specially, as a panicky abort if (VG_STREQ(expr, "0")) { VG_(printf)("\n%s: %s:%d (%s): the 'impossible' happened.\n", component, file, line, fn ); } else { VG_(printf)("\n%s: %s:%d (%s): Assertion '%s' failed.\n", component, file, line, fn, expr ); } if (!VG_STREQ(buf, "")) VG_(printf)("%s: %s\n", component, buf ); report_and_quit(bugs_to, NULL); }
static void gen_suppression(Error* err) { Int i; static UChar buf[M_VG_ERRTXT]; Bool main_done = False; ExeContext* ec = VG_(get_error_where)(err); Int stop_at = VG_(clo_backtrace_size); /* At most VG_N_SUPP_CALLERS names */ if (stop_at > VG_N_SUPP_CALLERS) stop_at = VG_N_SUPP_CALLERS; vg_assert(stop_at > 0); VG_(printf)("{\n"); VG_(printf)(" <insert a suppression name here>\n"); if (PThreadErr == err->ekind) { VG_(printf)(" core:PThread\n"); } else { Char* name = SK_(get_error_name)(err); if (NULL == name) { VG_(message)(Vg_UserMsg, "(tool does not allow error to be suppressed)"); return; } VG_(printf)(" %s:%s\n", VG_(details).name, name); SK_(print_extra_suppression_info)(err); } /* This loop condensed from VG_(mini_stack_dump)() */ i = 0; do { Addr eip = ec->eips[i]; if (i > 0) eip--; /* point to calling line */ if ( VG_(get_fnname_nodemangle) (eip, buf, M_VG_ERRTXT) ) { // Stop after "main"; if main() is recursive, stop after last main(). if ( ! VG_(clo_show_below_main)) { if (VG_STREQ(buf, "main")) main_done = True; else if (main_done) break; } VG_(printf)(" fun:%s\n", buf); } else if ( VG_(get_objname)(eip, buf, M_VG_ERRTXT) ) { VG_(printf)(" obj:%s\n", buf); } else { VG_(printf)(" ???:??? " "# unknown, suppression will not work, sorry\n"); } i++; } while (i < stop_at && ec->eips[i] != 0); VG_(printf)("}\n"); }
Bool MAC_(shared_recognised_suppression) ( Char* name, Supp* su ) { SuppKind skind; if (VG_STREQ(name, "Param")) skind = ParamSupp; else if (VG_STREQ(name, "CoreMem")) skind = CoreMemSupp; else if (VG_STREQ(name, "Addr1")) skind = Addr1Supp; else if (VG_STREQ(name, "Addr2")) skind = Addr2Supp; else if (VG_STREQ(name, "Addr4")) skind = Addr4Supp; else if (VG_STREQ(name, "Addr8")) skind = Addr8Supp; else if (VG_STREQ(name, "Addr16")) skind = Addr16Supp; else if (VG_STREQ(name, "Free")) skind = FreeSupp; else if (VG_STREQ(name, "Leak")) skind = LeakSupp; else if (VG_STREQ(name, "Overlap")) skind = OverlapSupp; else return False; VG_(set_supp_kind)(su, skind); return True; }
void VG_(assert_fail) ( Bool isCore, const Char* expr, const Char* file, Int line, const Char* fn, const HChar* format, ... ) { va_list vargs; Char buf[256]; Char* component; Char* bugs_to; static Bool entered = False; if (entered) VG_(exit)(2); entered = True; va_start(vargs, format); VG_(vsprintf) ( buf, format, vargs ); va_end(vargs); if (isCore) { component = "valgrind"; bugs_to = VG_BUGS_TO; } else { component = VG_(details).name; bugs_to = VG_(details).bug_reports_to; } if (VG_(clo_xml)) VG_(message)(Vg_UserMsg, "</valgrindoutput>\n"); // Treat vg_assert2(0, "foo") specially, as a panicky abort if (VG_STREQ(expr, "0")) { VG_(printf)("\n%s: %s:%d (%s): the 'impossible' happened.\n", component, file, line, fn, expr ); } else { VG_(printf)("\n%s: %s:%d (%s): Assertion '%s' failed.\n", component, file, line, fn, expr ); } if (!VG_STREQ(buf, "")) VG_(printf)("%s: %s\n", component, buf ); report_and_quit(bugs_to, 0,0,0,0); }
static Bool is_hash_bang_file(Char* f) { SysRes res = VG_(open)(f, VKI_O_RDONLY, 0); if (!sr_isError(res)) { Char buf[3] = {0,0,0}; Int fd = sr_Res(res); Int n = VG_(read)(fd, buf, 2); if (n == 2 && VG_STREQ("#!", buf)) return True; } return False; }
void VG_(apply_StackTrace)( void(*action)(UInt n, Addr ip), StackTrace ips, UInt n_ips ) { #define MYBUF_LEN 50 // only needs to be long enough for // the names specially tested for Bool main_done = False; Char mybuf[MYBUF_LEN]; // ok to stack allocate mybuf[] -- it's tiny Int i = 0; vg_assert(n_ips > 0); do { Addr ip = ips[i]; if (i > 0) ip -= VG_MIN_INSTR_SZB; // point to calling line // Stop after the first appearance of "main" or one of the other names // (the appearance of which is a pretty good sign that we've gone past // main without seeing it, for whatever reason) if ( ! VG_(clo_show_below_main)) { VG_(get_fnname_nodemangle)( ip, mybuf, MYBUF_LEN ); mybuf[MYBUF_LEN-1] = 0; // paranoia if ( VG_STREQ("main", mybuf) # if defined(VGO_linux) || VG_STREQ("__libc_start_main", mybuf) // glibc glibness || VG_STREQ("generic_start_main", mybuf) // Yellow Dog doggedness # endif ) main_done = True; } // Act on the ip action(i, ip); i++; } while (i < n_ips && ips[i] != 0 && !main_done); #undef MYBUF_LEN }
void VG_(assert_fail) ( Bool isCore, const HChar* expr, const HChar* file, Int line, const HChar* fn, const HChar* format, ... ) { va_list vargs, vargs_copy; const HChar* component; const HChar* bugs_to; UInt written; static Bool entered = False; if (entered) VG_(exit)(2); entered = True; if (isCore) { component = "valgrind"; bugs_to = VG_BUGS_TO; } else { component = VG_(details).name; bugs_to = VG_(details).bug_reports_to; } if (VG_(clo_xml)) VG_(printf_xml)("</valgrindoutput>\n"); // Treat vg_assert2(0, "foo") specially, as a panicky abort if (VG_STREQ(expr, "0")) { VG_(printf)("\n%s: %s:%d (%s): the 'impossible' happened.\n", component, file, line, fn ); } else { VG_(printf)("\n%s: %s:%d (%s): Assertion '%s' failed.\n", component, file, line, fn, expr ); } /* Check whether anything will be written */ HChar buf[5]; va_start(vargs, format); va_copy(vargs_copy, vargs); written = VG_(vsnprintf) ( buf, sizeof(buf), format, vargs ); va_end(vargs); if (written > 0) { VG_(printf)("%s: ", component); VG_(vprintf)(format, vargs_copy); VG_(printf)("\n"); } report_and_quit(bugs_to, NULL); }
// Returns NULL if it wasn't found. const HChar* ML_(find_executable) ( const HChar* exec ) { vg_assert(NULL != exec); if (VG_(strchr)(exec, '/')) { // Has a '/' - use the name as is VG_(strncpy)( executable_name_out, exec, VKI_PATH_MAX-1 ); } else { // No '/' - we need to search the path HChar* path; VG_(strncpy)( executable_name_in, exec, VKI_PATH_MAX-1 ); VG_(memset) ( executable_name_out, 0, VKI_PATH_MAX ); path = VG_(getenv)("PATH"); scan_colsep(path, match_executable); } return VG_STREQ(executable_name_out, "") ? NULL : executable_name_out; }
/* Read suppressions from the file specified in VG_(clo_suppressions) and place them in the suppressions list. If there's any difficulty doing this, just give up -- there's no point in trying to recover. */ static void load_one_suppressions_file ( Char* filename ) { # define N_BUF 200 SysRes sres; Int fd, i; Bool eof; Char buf[N_BUF+1]; Char* tool_names; Char* supp_name; Char* err_str = NULL; SuppLoc tmp_callers[VG_MAX_SUPP_CALLERS]; fd = -1; sres = VG_(open)( filename, VKI_O_RDONLY, 0 ); if (sres.isError) { if (VG_(clo_xml)) VG_(message)(Vg_UserMsg, "</valgrindoutput>\n"); VG_(message)(Vg_UserMsg, "FATAL: can't open suppressions file '%s'", filename ); VG_(exit)(1); } fd = sres.res; # define BOMB(S) { err_str = S; goto syntax_error; } while (True) { /* Assign and initialise the two suppression halves (core and tool) */ Supp* supp; supp = VG_(arena_malloc)(VG_AR_CORE, sizeof(Supp)); supp->count = 0; // Initialise temporary reading-in buffer. for (i = 0; i < VG_MAX_SUPP_CALLERS; i++) { tmp_callers[i].ty = NoName; tmp_callers[i].name = NULL; } supp->string = supp->extra = NULL; eof = VG_(get_line) ( fd, buf, N_BUF ); if (eof) break; if (!VG_STREQ(buf, "{")) BOMB("expected '{' or end-of-file"); eof = VG_(get_line) ( fd, buf, N_BUF ); if (eof || VG_STREQ(buf, "}")) BOMB("unexpected '}'"); supp->sname = VG_(arena_strdup)(VG_AR_CORE, buf); eof = VG_(get_line) ( fd, buf, N_BUF ); if (eof) BOMB("unexpected end-of-file"); /* Check it has the "tool1,tool2,...:supp" form (look for ':') */ i = 0; while (True) { if (buf[i] == ':') break; if (buf[i] == '\0') BOMB("malformed 'tool1,tool2,...:supp' line"); i++; } buf[i] = '\0'; /* Replace ':', splitting into two strings */ tool_names = & buf[0]; supp_name = & buf[i+1]; if (VG_(needs).core_errors && tool_name_present("core", tool_names)) { // A core suppression //(example code, see comment on CoreSuppKind above) //if (VG_STREQ(supp_name, "Thread")) // supp->skind = ThreadSupp; //else BOMB("unknown core suppression type"); } else if (VG_(needs).tool_errors && tool_name_present(VG_(details).name, tool_names)) { // A tool suppression if (VG_TDICT_CALL(tool_recognised_suppression, supp_name, supp)) { /* Do nothing, function fills in supp->skind */ } else { BOMB("unknown tool suppression type"); } } else { // Ignore rest of suppression while (True) { eof = VG_(get_line) ( fd, buf, N_BUF ); if (eof) BOMB("unexpected end-of-file"); if (VG_STREQ(buf, "}")) break; } continue; } if (VG_(needs).tool_errors && !VG_TDICT_CALL(tool_read_extra_suppression_info, fd, buf, N_BUF, supp)) { BOMB("bad or missing extra suppression info"); } i = 0; while (True) { eof = VG_(get_line) ( fd, buf, N_BUF ); if (eof) BOMB("unexpected end-of-file"); if (VG_STREQ(buf, "}")) { if (i > 0) { break; } else { BOMB("missing stack trace"); } } if (i == VG_MAX_SUPP_CALLERS) BOMB("too many callers in stack trace"); if (i > 0 && i >= VG_(clo_backtrace_size)) break; tmp_callers[i].name = VG_(arena_strdup)(VG_AR_CORE, buf); if (!setLocationTy(&(tmp_callers[i]))) BOMB("location should start with 'fun:' or 'obj:'"); i++; } // If the num callers is >= VG_(clo_backtrace_size), ignore any extra // lines and grab the '}'. if (!VG_STREQ(buf, "}")) { do { eof = VG_(get_line) ( fd, buf, N_BUF ); } while (!eof && !VG_STREQ(buf, "}")); } // Copy tmp_callers[] into supp->callers[] supp->n_callers = i; supp->callers = VG_(arena_malloc)(VG_AR_CORE, i*sizeof(SuppLoc)); for (i = 0; i < supp->n_callers; i++) { supp->callers[i] = tmp_callers[i]; } supp->next = suppressions; suppressions = supp; } VG_(close)(fd); return; syntax_error: if (VG_(clo_xml)) VG_(message)(Vg_UserMsg, "</valgrindoutput>\n"); VG_(message)(Vg_UserMsg, "FATAL: in suppressions file '%s': %s", filename, err_str ); VG_(close)(fd); VG_(message)(Vg_UserMsg, "exiting now."); VG_(exit)(1); # undef BOMB # undef N_BUF }
void VG_(split_up_argv)( Int argc, HChar** argv ) { Int i; Bool augment = True; static Bool already_called = False; XArrayStrings tmp_xarray = {0,0,NULL}; /* This function should be called once, at startup, and then never again. */ vg_assert(!already_called); already_called = True; /* Collect up the args-for-V. */ i = 1; /* skip the exe (stage2) name. */ for (; i < argc; i++) { vg_assert(argv[i]); if (0 == VG_(strcmp)(argv[i], "--")) { i++; break; } if (0 == VG_(strcmp)(argv[i], "--command-line-only=yes")) augment = False; if (argv[i][0] != '-') break; add_string( &tmp_xarray, argv[i] ); } /* Should now be looking at the exe name. */ if (i < argc) { vg_assert(argv[i]); VG_(args_the_exename) = argv[i]; i++; } /* The rest are args for the client. */ for (; i < argc; i++) { vg_assert(argv[i]); add_string( &VG_(args_for_client), argv[i] ); } VG_(args_for_valgrind).size = 0; VG_(args_for_valgrind).used = 0; VG_(args_for_valgrind).strs = NULL; /* Get extra args from ~/.valgrindrc, $VALGRIND_OPTS and ./.valgrindrc into VG_(args_for_valgrind). */ if (augment) { // read_dot_valgrindrc() allocates the return value with // VG_(malloc)(). We do not free f1_clo and f2_clo as they get // put into VG_(args_for_valgrind) and so must persist. HChar* home = VG_(getenv)("HOME"); HChar* f1_clo = home ? read_dot_valgrindrc( home ) : NULL; HChar* env_clo = VG_(strdup)( VG_(getenv)(VALGRIND_OPTS) ); HChar* f2_clo = NULL; // Don't read ./.valgrindrc if "." is the same as "$HOME", else its // contents will be applied twice. (bug #142488) if (home) { HChar cwd[VKI_PATH_MAX+1]; Bool cwd_ok = VG_(getcwd)(cwd, VKI_PATH_MAX); f2_clo = ( (cwd_ok && VG_STREQ(home, cwd)) ? NULL : read_dot_valgrindrc(".") ); } if (f1_clo) add_args_from_string( f1_clo ); if (env_clo) add_args_from_string( env_clo ); if (f2_clo) add_args_from_string( f2_clo ); } /* .. and record how many extras we got. */ VG_(args_for_valgrind_noexecpass) = VG_(args_for_valgrind).used; /* Finally, copy tmp_xarray onto the end. */ for (i = 0; i < tmp_xarray.used; i++) add_string( &VG_(args_for_valgrind), tmp_xarray.strs[i] ); if (tmp_xarray.strs) VG_(free)(tmp_xarray.strs); }
void test_VG_STREQ(void) { CHECK( ! VG_STREQ(NULL, NULL) ); CHECK( ! VG_STREQ(NULL, "ab") ); CHECK( ! VG_STREQ("ab", NULL) ); CHECK( ! VG_STREQ("", "a") ); CHECK( ! VG_STREQ("a", "") ); CHECK( ! VG_STREQ("abc", "abcd")); CHECK( ! VG_STREQ("abcd", "abc") ); CHECK( ! VG_STREQ("Abcd", "abcd")); CHECK( ! VG_STREQ("abcd", "Abcd")); CHECK( VG_STREQ("", "") ); CHECK( VG_STREQ("a", "a") ); CHECK( VG_STREQ("abcd", "abcd") ); }
// Prints a .disambig file entry for a given variable // This consists of 2 lines: // variable name, disambig type // e.g., // /foo <-- variable name // S <-- disambig type static TraversalResult printDisambigAction(VariableEntry* var, char* varName, VariableOrigin varOrigin, UInt numDereferences, UInt layersBeforeBase, Bool overrideIsInit, DisambigOverride disambigOverride, Bool isSequence, Addr pValue, Addr pValueGuest, Addr* pValueArray, Addr* pValueArrayGuest, UInt numElts, FunctionEntry* varFuncInfo, Bool isEnter) { /* silence unused variable warnings */ (void)varOrigin; (void)numDereferences; (void)layersBeforeBase; (void)overrideIsInit; (void)disambigOverride; (void)isSequence; (void)pValue; (void)pValueArray; (void)numElts; (void)varFuncInfo; (void)isEnter; (void)pValueGuest; (void)pValueArrayGuest; // If this is not a variable that's worthy of being outputted to the // .disambig file, then punt: if (!shouldOutputVarToDisambig(var)) { // We do not want to traverse any further than the surface level // for .disambig: return STOP_TRAVERSAL; } // Line 1: Variable name fputs(varName, disambig_fp); fputs("\n", disambig_fp); // Line 2: Disambig type /* Default values: Base type "char" and "unsigned char": 'I' for integer Pointer to "char": 'S' for string Pointer to all other types: - 'A' for array if var->disambigMultipleElts, which means that we've observed array behavior during program's execution or if !var->pointerHasEverBeenObserved, which means that the pointer has never been observed so a conservative guess of 'A' should be the default or if var->isStructUnionMember - don't try to be smart about member variables within structs/unions - just default to "A" - 'P' for pointer if (var->pointerHasEverBeenObserved && !var->disambigMultipleElts) */ if (0 == var->ptrLevels) { if ((D_CHAR == var->varType->decType) || (D_UNSIGNED_CHAR == var->varType->decType)) { fputs("I", disambig_fp); } } // Special case for C++ 'this' parameter - always make it 'P' else if (VG_STREQ(var->name, "this")) { fputs("P", disambig_fp); } // Normal string, not pointer to string else if (IS_STRING(var) && (1 == var->ptrLevels)) { fputs("S", disambig_fp); } else if (var->ptrLevels > 0) { if (IS_MEMBER_VAR(var)) { fputs("A", disambig_fp); } else { if (var->pointerHasEverBeenObserved) { if (var->disambigMultipleElts) { fputs("A", disambig_fp); } else { fputs("P", disambig_fp); } } // default behavior for variable that was // never observed during the execution else { fputs("A", disambig_fp); } } } fputs("\n", disambig_fp); // We do not want to traverse any further than the surface level for // .disambig: return STOP_TRAVERSAL; }
/* This is an example of a variables output format: ----SECTION---- globals StaticArraysTest_c/staticStrings StaticArraysTest_c/staticStrings[] StaticArraysTest_c/staticShorts StaticArraysTest_c/staticShorts[] ----SECTION---- ..f() arg strings strings[] return ----SECTION---- ..b() oneShort manyShorts manyShorts[] return ----SECTION---- ..main() return */ void initializeVarsTree() { char nextLineIsFunction = 0; FunctionTree* currentFunctionTree = 0; while (fgets(input_line, 200, trace_vars_input_fp)) { int lineLen = VG_(strlen)(input_line); // Skip blank lines (those consisting of solely the newline character) // Also skip comment lines (those beginning with COMMENT_CHAR) if(('\n' == input_line[0]) || (COMMENT_CHAR == input_line[0])) { // VG_(printf)("skipping blank line ...\n"); continue; } // Strip '\n' off the end of the line // NOTE: Only do this if the end of the line is a newline character. // If the very last line of a file is not followed by a newline character, // then blindly stripping off the last character will truncate the actual // string, which is undesirable. if (input_line[lineLen - 1] == '\n') { input_line[lineLen - 1] = '\0'; } if (VG_STREQ(input_line, ENTRY_DELIMETER)) { nextLineIsFunction = 1; } else { // Create a new FunctionTree and insert it into vars_tree if (nextLineIsFunction) { currentFunctionTree = VG_(malloc)("fjalar_selec.c: initVT", sizeof(*currentFunctionTree)); currentFunctionTree->function_fjalar_name = VG_(strdup)("fjalar_selec.c: initVT.2", input_line); currentFunctionTree->function_variables_tree = NULL; // Remember to initialize to null! tsearch((void*)currentFunctionTree, (void**)&vars_tree, compareFunctionTrees); // Keep a special pointer for global variables to trace if (VG_STREQ(input_line, GLOBAL_STRING)) { globalFunctionTree = currentFunctionTree; // VG_(printf)("globalFunctionTree: %p\n", globalFunctionTree); } else { // VG_(printf)("Function: %s\n", currentFunctionTree->function_fjalar_name); } } // Otherwise, create a new variable and stuff it into // the function_variables_tree of the current function_tree else { char* newString = VG_(strdup)("fjalar_selec.c: initVT.3", input_line); tsearch((void*)newString, (void**)&(currentFunctionTree->function_variables_tree), compareStrings); // VG_(printf)("variable: %s\n", newString); } nextLineIsFunction = 0; } } fclose(trace_vars_input_fp); trace_vars_input_fp = 0; }
/* Read suppressions from the file specified in vg_clo_suppressions and place them in the suppressions list. If there's any difficulty doing this, just give up -- there's no point in trying to recover. */ static void load_one_suppressions_file ( Char* filename ) { # define N_BUF 200 Int fd, i; Bool eof, too_many_contexts = False; Char buf[N_BUF+1]; Char* tool_names; Char* supp_name; fd = VG_(open)( filename, VKI_O_RDONLY, 0 ); if (fd < 0) { VG_(message)(Vg_UserMsg, "FATAL: can't open suppressions file `%s'", filename ); VG_(exit)(1); } while (True) { /* Assign and initialise the two suppression halves (core and tool) */ Supp* supp; supp = VG_(arena_malloc)(VG_AR_CORE, sizeof(Supp)); supp->count = 0; for (i = 0; i < VG_N_SUPP_CALLERS; i++) supp->caller[i] = NULL; supp->string = supp->extra = NULL; eof = VG_(get_line) ( fd, buf, N_BUF ); if (eof) break; if (!VG_STREQ(buf, "{")) goto syntax_error; eof = VG_(get_line) ( fd, buf, N_BUF ); if (eof || VG_STREQ(buf, "}")) goto syntax_error; supp->sname = VG_(arena_strdup)(VG_AR_CORE, buf); eof = VG_(get_line) ( fd, buf, N_BUF ); if (eof) goto syntax_error; /* Check it has the "skin1,skin2,...:supp" form (look for ':') */ i = 0; while (True) { if (buf[i] == ':') break; if (buf[i] == '\0') goto syntax_error; i++; } buf[i] = '\0'; /* Replace ':', splitting into two strings */ tool_names = & buf[0]; supp_name = & buf[i+1]; /* Is it a core suppression? */ if (VG_(needs).core_errors && tool_name_present("core", tool_names)) { if (VG_STREQ(supp_name, "PThread")) supp->skind = PThreadSupp; else goto syntax_error; } /* Is it a tool suppression? */ else if (VG_(needs).skin_errors && tool_name_present(VG_(details).name, tool_names)) { if (SK_(recognised_suppression)(supp_name, supp)) { /* Do nothing, function fills in supp->skind */ } else goto syntax_error; } else { /* Ignore rest of suppression */ while (True) { eof = VG_(get_line) ( fd, buf, N_BUF ); if (eof) goto syntax_error; if (VG_STREQ(buf, "}")) break; } continue; } if (VG_(needs).skin_errors && !SK_(read_extra_suppression_info)(fd, buf, N_BUF, supp)) goto syntax_error; /* "i > 0" ensures at least one caller read. */ for (i = 0; i <= VG_N_SUPP_CALLERS; i++) { eof = VG_(get_line) ( fd, buf, N_BUF ); if (eof) goto syntax_error; if (i > 0 && VG_STREQ(buf, "}")) break; if (i == VG_N_SUPP_CALLERS) break; supp->caller[i] = VG_(arena_strdup)(VG_AR_CORE, buf); if (!setLocationTy(&(supp->caller[i]), &(supp->caller_ty[i]))) goto syntax_error; } /* make sure to grab the '}' if the num callers is >= VG_N_SUPP_CALLERS */ if (!VG_STREQ(buf, "}")) { // Don't just ignore extra lines -- abort. (Someone complained // about silent ignoring of lines in bug #77922.) //do { // eof = VG_(get_line) ( fd, buf, N_BUF ); //} while (!eof && !VG_STREQ(buf, "}")); too_many_contexts = True; goto syntax_error; } supp->next = vg_suppressions; vg_suppressions = supp; } VG_(close)(fd); return; syntax_error: if (eof) { VG_(message)(Vg_UserMsg, "FATAL: in suppressions file `%s': unexpected EOF", filename ); } else if (too_many_contexts) { VG_(message)(Vg_UserMsg, "FATAL: in suppressions file: `%s': at %s:", filename, buf ); VG_(message)(Vg_UserMsg, "too many lines (limit of %d contexts in suppressions)", VG_N_SUPP_CALLERS); } else { VG_(message)(Vg_UserMsg, "FATAL: in suppressions file: `%s': syntax error on: %s", filename, buf ); } VG_(close)(fd); VG_(message)(Vg_UserMsg, "exiting now."); VG_(exit)(1); # undef N_BUF }
void test_VG_STREQ(void) { CHECK( ! VG_STREQ(NULL, NULL) ); // Nb: strcmp() considers these equal CHECK( ! VG_STREQ(NULL, "ab") ); // Nb: strcmp() seg faults on this CHECK( ! VG_STREQ("ab", NULL) ); // Nb: strcmp() seg faults on this CHECK( ! VG_STREQ("", "a") ); CHECK( ! VG_STREQ("a", "") ); CHECK( ! VG_STREQ("abc", "abcd")); CHECK( ! VG_STREQ("abcd", "abc") ); CHECK( ! VG_STREQ("Abcd", "abcd")); CHECK( ! VG_STREQ("abcd", "Abcd")); CHECK( VG_STREQ("", "") ); CHECK( VG_STREQ("a", "a") ); CHECK( VG_STREQ("abcd", "abcd") ); }
// Copies the string, prepending it with the startup working directory, and // expanding %p and %q entries. Returns a new, malloc'd string. Char* VG_(expand_file_name)(Char* option_name, Char* format) { static Char base_dir[VKI_PATH_MAX]; Int len, i = 0, j = 0; Char* out; Bool ok = VG_(get_startup_wd)(base_dir, VKI_PATH_MAX); tl_assert(ok); if (VG_STREQ(format, "")) { // Empty name, bad. VG_(umsg)("%s: filename is empty", option_name); goto bad; } // If 'format' starts with a '~', abort -- the user probably expected the // shell to expand but it didn't (see bug 195268 for details). This means // that we don't allow a legitimate filename beginning with '~' but that // seems very unlikely. if (format[0] == '~') { VG_(umsg)("%s: filename begins with '~'\n", option_name); VG_(umsg)("You probably expected the shell to expand the '~', but it\n"); VG_(umsg)("didn't. The rules for '~'-expansion " "vary from shell to shell.\n"); VG_(umsg)("You might have more luck using $HOME instead.\n"); goto bad; } // If 'format' starts with a '/', do not prefix with startup dir. if (format[0] != '/') { j += VG_(strlen)(base_dir); } // The 10 is slop, it should be enough in most cases. len = j + VG_(strlen)(format) + 10; out = VG_(malloc)( "options.efn.1", len ); if (format[0] != '/') { VG_(strcpy)(out, base_dir); out[j++] = '/'; } #define ENSURE_THIS_MUCH_SPACE(x) \ if (j + x >= len) { \ len += (10 + x); \ out = VG_(realloc)("options.efn.2(multiple)", out, len); \ } while (format[i]) { if (format[i] != '%') { ENSURE_THIS_MUCH_SPACE(1); out[j++] = format[i++]; } else { // We saw a '%'. What's next... i++; if ('%' == format[i]) { // Replace '%%' with '%'. ENSURE_THIS_MUCH_SPACE(1); out[j++] = format[i++]; } else if ('p' == format[i]) { // Print the PID. Assume that it's not longer than 10 chars -- // reasonable since 'pid' is an Int (ie. 32 bits). Int pid = VG_(getpid)(); ENSURE_THIS_MUCH_SPACE(10); j += VG_(sprintf)(&out[j], "%d", pid); i++; } else if ('q' == format[i]) { i++; if ('{' == format[i]) { // Get the env var name, print its contents. Char* qualname; Char* qual; i++; qualname = &format[i]; while (True) { if (0 == format[i]) { VG_(message)(Vg_UserMsg, "%s: malformed %%q specifier\n", option_name); goto bad; } else if ('}' == format[i]) { // Temporarily replace the '}' with NUL to extract var // name. format[i] = 0; qual = VG_(getenv)(qualname); if (NULL == qual) { VG_(message)(Vg_UserMsg, "%s: environment variable %s is not set\n", option_name, qualname); format[i] = '}'; // Put the '}' back. goto bad; } format[i] = '}'; // Put the '}' back. i++; break; } i++; } ENSURE_THIS_MUCH_SPACE(VG_(strlen)(qual)); j += VG_(sprintf)(&out[j], "%s", qual); } else { VG_(message)(Vg_UserMsg, "%s: expected '{' after '%%q'\n", option_name); goto bad; } } else { // Something else, abort. VG_(message)(Vg_UserMsg, "%s: expected 'p' or 'q' or '%%' after '%%'\n", option_name); goto bad; } } } ENSURE_THIS_MUCH_SPACE(1); out[j++] = 0; return out; bad: { Char* opt = // 2: 1 for the '=', 1 for the NUL. VG_(malloc)( "options.efn.3", VG_(strlen)(option_name) + VG_(strlen)(format) + 2 ); VG_(strcpy)(opt, option_name); VG_(strcat)(opt, "="); VG_(strcat)(opt, format); VG_(err_bad_option)(opt); } }
// Copies the string, prepending it with the startup working directory, and // expanding %p and %q entries. Returns a new, malloc'd string. HChar* VG_(expand_file_name)(const HChar* option_name, const HChar* format) { const HChar *base_dir; Int len, i = 0, j = 0; HChar* out; const HChar *message = NULL; base_dir = VG_(get_startup_wd)(); if (VG_STREQ(format, "")) { // Empty name, bad. message = "No filename given\n"; goto bad; } // If 'format' starts with a '~', abort -- the user probably expected the // shell to expand but it didn't (see bug 195268 for details). This means // that we don't allow a legitimate filename beginning with '~' but that // seems very unlikely. if (format[0] == '~') { message = "Filename begins with '~'\n" "You probably expected the shell to expand the '~', but it\n" "didn't. The rules for '~'-expansion vary from shell to shell.\n" "You might have more luck using $HOME instead.\n"; goto bad; } len = VG_(strlen)(format) + 1; out = VG_(malloc)( "options.efn.1", len ); #define ENSURE_THIS_MUCH_SPACE(x) \ if (j + x >= len) { \ len += (10 + x); \ out = VG_(realloc)("options.efn.2(multiple)", out, len); \ } while (format[i]) { if (format[i] != '%') { ENSURE_THIS_MUCH_SPACE(1); out[j++] = format[i++]; } else { // We saw a '%'. What's next... i++; if ('%' == format[i]) { // Replace '%%' with '%'. ENSURE_THIS_MUCH_SPACE(1); out[j++] = format[i++]; } else if ('p' == format[i]) { // Print the PID. Assume that it's not longer than 10 chars -- // reasonable since 'pid' is an Int (ie. 32 bits). Int pid = VG_(getpid)(); ENSURE_THIS_MUCH_SPACE(10); j += VG_(sprintf)(&out[j], "%d", pid); i++; } else if ('n' == format[i]) { // Print a seq nr. static Int last_pid; static Int seq_nr; Int pid = VG_(getpid)(); if (last_pid != pid) seq_nr = 0; last_pid = pid; seq_nr++; ENSURE_THIS_MUCH_SPACE(10); j += VG_(sprintf)(&out[j], "%d", seq_nr); i++; } else if ('q' == format[i]) { i++; if ('{' == format[i]) { // Get the env var name, print its contents. HChar *qual; Int begin_qualname = ++i; while (True) { if (0 == format[i]) { message = "Missing '}' in %q specifier\n"; goto bad; } else if ('}' == format[i]) { Int qualname_len = i - begin_qualname; HChar qualname[qualname_len + 1]; VG_(strncpy)(qualname, format + begin_qualname, qualname_len); qualname[qualname_len] = '\0'; qual = VG_(getenv)(qualname); if (NULL == qual) { // This memory will leak, But we don't care because // VG_(fmsg_bad_option) will terminate the process. HChar *str = VG_(malloc)("options.efn.3", 100 + qualname_len); VG_(sprintf)(str, "Environment variable '%s' is not set\n", qualname); message = str; goto bad; } i++; break; } i++; } ENSURE_THIS_MUCH_SPACE(VG_(strlen)(qual)); j += VG_(sprintf)(&out[j], "%s", qual); } else { message = "Expected '{' after '%q'\n"; goto bad; } } else { // Something else, abort. message = "Expected 'p' or 'q' or '%' after '%'\n"; goto bad; } } } ENSURE_THIS_MUCH_SPACE(1); out[j++] = 0; // If 'out' is not an absolute path name, prefix it with the startup dir. if (out[0] != '/') { if (base_dir == NULL) { message = "Current working dir doesn't exist, use absolute path\n"; goto bad; } len = VG_(strlen)(base_dir) + 1 + VG_(strlen)(out) + 1; HChar *absout = VG_(malloc)("options.efn.4", len); VG_(strcpy)(absout, base_dir); VG_(strcat)(absout, "/"); VG_(strcat)(absout, out); VG_(free)(out); out = absout; } return out; bad: { vg_assert(message != NULL); // 2: 1 for the '=', 1 for the NUL. HChar opt[VG_(strlen)(option_name) + VG_(strlen)(format) + 2]; VG_(sprintf)(opt, "%s=%s", option_name, format); VG_(fmsg_bad_option)(opt, "%s", message); } }
/* Read a symbol table (nlist). Add the resulting candidate symbols to 'syms'; the caller will post-process them and hand them off to ML_(addSym) itself. */ static void read_symtab( /*OUT*/XArray* /* DiSym */ syms, struct _DebugInfo* di, struct NLIST* o_symtab, UInt o_symtab_count, UChar* o_strtab, UInt o_strtab_sz ) { Int i; Addr sym_addr; DiSym risym; UChar* name; static UChar* s_a_t_v = NULL; /* do not make non-static */ for (i = 0; i < o_symtab_count; i++) { struct NLIST *nl = o_symtab+i; if ((nl->n_type & N_TYPE) == N_SECT) { sym_addr = di->text_bias + nl->n_value; /*} else if ((nl->n_type & N_TYPE) == N_ABS) { GrP fixme don't ignore absolute symbols? sym_addr = nl->n_value; */ } else { continue; } if (di->trace_symtab) VG_(printf)("nlist raw: avma %010lx %s\n", sym_addr, o_strtab + nl->n_un.n_strx ); /* If no part of the symbol falls within the mapped range, ignore it. */ if (sym_addr <= di->text_avma || sym_addr >= di->text_avma+di->text_size) { continue; } /* skip names which point outside the string table; following these risks segfaulting Valgrind */ name = o_strtab + nl->n_un.n_strx; if (name < o_strtab || name >= o_strtab + o_strtab_sz) continue; /* skip nameless symbols; these appear to be common, but useless */ if (*name == 0) continue; risym.tocptr = 0; risym.addr = sym_addr; risym.size = // let canonicalize fix it di->text_avma+di->text_size - sym_addr; risym.name = ML_(addStr)(di, name, -1); risym.isText = True; // Lots of user function names get prepended with an underscore. Eg. the // function 'f' becomes the symbol '_f'. And the "below main" // function is called "start". So we skip the leading underscore, and // if we see 'start' and --show-below-main=no, we rename it as // "start_according_to_valgrind", which makes it easy to spot later // and display as "(below main)". if (risym.name[0] == '_') { risym.name++; } else if (!VG_(clo_show_below_main) && VG_STREQ(risym.name, "start")) { if (s_a_t_v == NULL) s_a_t_v = ML_(addStr)(di, "start_according_to_valgrind", -1); vg_assert(s_a_t_v); risym.name = s_a_t_v; } vg_assert(risym.name); VG_(addToXA)( syms, &risym ); } }
Bool VG_(split_up_argv)( Int argc, HChar** argv ) { Int i; Bool augment = True; static Bool already_called = False; HChar* hwArgs = HARDWIRED_ARGS_FOR_BGQ; if (hwArgs) { // This is never freed. The strduping is necessary because // hwArgs is subsequently modified. hwArgs = VG_(strdup)("commandline.sua.5", hwArgs); } XArray* /* of HChar* */ tmp_xarray; /* This function should be called once, at startup, and then never again. */ vg_assert(!already_called); already_called = True; tmp_xarray = VG_(newXA)( VG_(malloc), "commandline.sua.1", VG_(free), sizeof(HChar*) ); vg_assert(tmp_xarray); vg_assert( ! VG_(args_for_valgrind) ); VG_(args_for_valgrind) = VG_(newXA)( VG_(malloc), "commandline.sua.2", VG_(free), sizeof(HChar*) ); vg_assert( VG_(args_for_valgrind) ); vg_assert( ! VG_(args_for_client) ); VG_(args_for_client) = VG_(newXA)( VG_(malloc), "commandline.sua.3", VG_(free), sizeof(HChar*) ); vg_assert( VG_(args_for_client) ); /* Collect up the args-for-V. */ i = 1; /* skip the exe (stage2) name. */ for (; i < argc; i++) { vg_assert(argv[i]); if (hwArgs != NULL) { break; } if (0 == VG_(strcmp)(argv[i], "--")) { i++; break; } if (0 == VG_(strcmp)(argv[i], "--command-line-only=yes")) augment = False; # if !defined(VGPV_ppc64_linux_bgq) /* If we find an arg which doesn't start with '-', assume it is the executable name, so stop copying args for Valgrind at this point. Except on statically-linked-in scenarios (BGQ), in which case the args for V must be terminated by "--", and the first arg that follows is the first arg for the client. */ if (argv[i][0] != '-') break; # endif add_string( tmp_xarray, argv[i] ); } /* Set VG_(args_the_exename). Different in the statically-linked-in case (BGQ). */ # if defined(VGPV_ppc64_linux_bgq) vg_assert(!VG_(args_the_exename)); vg_assert(argv[0]); VG_(args_the_exename) = argv[0]; # else /* Should now be looking at the exe name. */ if (i < argc) { vg_assert(argv[i]); VG_(args_the_exename) = argv[i]; i++; } # endif /* The rest are args for the client. */ for (; i < argc; i++) { vg_assert(argv[i]); add_string( VG_(args_for_client), argv[i] ); } /* Get extra args from ~/.valgrindrc, $VALGRIND_OPTS and ./.valgrindrc into VG_(args_for_valgrind). */ if (augment) { // read_dot_valgrindrc() allocates the return value with // VG_(malloc)(). We do not free f1_clo and f2_clo as they get // put into VG_(args_for_valgrind) and so must persist. HChar* home = VG_(getenv)("HOME"); HChar* f1_clo = home ? read_dot_valgrindrc( home ) : NULL; HChar* env_clo = VG_(strdup)( "commandline.sua.4", VG_(getenv)(VALGRIND_OPTS) ); HChar* f2_clo = NULL; // Don't read ./.valgrindrc if "." is the same as "$HOME", else its // contents will be applied twice. (bug #142488) if (home) { HChar cwd[VKI_PATH_MAX+1]; Bool cwd_ok = VG_(get_startup_wd)(cwd, VKI_PATH_MAX); f2_clo = ( (cwd_ok && VG_STREQ(home, cwd)) ? NULL : read_dot_valgrindrc(".") ); } if (f1_clo) add_args_from_string( f1_clo ); if (env_clo) add_args_from_string( env_clo ); if (f2_clo) add_args_from_string( f2_clo ); if (hwArgs) add_args_from_string( hwArgs ); } /* .. and record how many extras we got. */ VG_(args_for_valgrind_noexecpass) = VG_(sizeXA)( VG_(args_for_valgrind) ); /* Finally, copy tmp_xarray onto the end. */ for (i = 0; i < VG_(sizeXA)( tmp_xarray ); i++) add_string( VG_(args_for_valgrind), * (HChar**)VG_(indexXA)( tmp_xarray, i ) ); VG_(deleteXA)( tmp_xarray ); return hwArgs != NULL; }
// Copies the string, prepending it with the startup working directory, and // expanding %p and %q entries. Returns a new, malloc'd string. HChar* VG_(expand_file_name)(const HChar* option_name, const HChar* format) { const HChar *base_dir; Int len, i = 0, j = 0; HChar* out; base_dir = VG_(get_startup_wd)(); if (VG_STREQ(format, "")) { // Empty name, bad. VG_(fmsg)("%s: filename is empty", option_name); goto bad; } // If 'format' starts with a '~', abort -- the user probably expected the // shell to expand but it didn't (see bug 195268 for details). This means // that we don't allow a legitimate filename beginning with '~' but that // seems very unlikely. if (format[0] == '~') { VG_(fmsg)( "%s: filename begins with '~'\n" "You probably expected the shell to expand the '~', but it\n" "didn't. The rules for '~'-expansion vary from shell to shell.\n" "You might have more luck using $HOME instead.\n", option_name ); goto bad; } len = VG_(strlen)(format) + 1; out = VG_(malloc)( "options.efn.1", len ); #define ENSURE_THIS_MUCH_SPACE(x) \ if (j + x >= len) { \ len += (10 + x); \ out = VG_(realloc)("options.efn.2(multiple)", out, len); \ } while (format[i]) { if (format[i] != '%') { ENSURE_THIS_MUCH_SPACE(1); out[j++] = format[i++]; } else { // We saw a '%'. What's next... i++; if ('%' == format[i]) { // Replace '%%' with '%'. ENSURE_THIS_MUCH_SPACE(1); out[j++] = format[i++]; } else if ('p' == format[i]) { // Print the PID. Assume that it's not longer than 10 chars -- // reasonable since 'pid' is an Int (ie. 32 bits). Int pid = VG_(getpid)(); ENSURE_THIS_MUCH_SPACE(10); j += VG_(sprintf)(&out[j], "%d", pid); i++; } else if ('q' == format[i]) { i++; if ('{' == format[i]) { // Get the env var name, print its contents. HChar *qual; Int begin_qualname = ++i; while (True) { if (0 == format[i]) { VG_(fmsg)("%s: malformed %%q specifier\n", option_name); goto bad; } else if ('}' == format[i]) { Int qualname_len = i - begin_qualname; HChar qualname[qualname_len + 1]; VG_(strncpy)(qualname, format + begin_qualname, qualname_len); qualname[qualname_len] = '\0'; qual = VG_(getenv)(qualname); if (NULL == qual) { VG_(fmsg)("%s: environment variable %s is not set\n", option_name, qualname); goto bad; } i++; break; } i++; } ENSURE_THIS_MUCH_SPACE(VG_(strlen)(qual)); j += VG_(sprintf)(&out[j], "%s", qual); } else { VG_(fmsg)("%s: expected '{' after '%%q'\n", option_name); goto bad; } } else { // Something else, abort. VG_(fmsg)("%s: expected 'p' or 'q' or '%%' after '%%'\n", option_name); goto bad; } } } ENSURE_THIS_MUCH_SPACE(1); out[j++] = 0; // If 'out' is not an absolute path name, prefix it with the startup dir. if (out[0] != '/') { len = VG_(strlen)(base_dir) + 1 + VG_(strlen)(out) + 1; HChar *absout = VG_(malloc)("options.efn.4", len); VG_(strcpy)(absout, base_dir); VG_(strcat)(absout, "/"); VG_(strcat)(absout, out); VG_(free)(out); out = absout; } return out; bad: { HChar* opt = // 2: 1 for the '=', 1 for the NUL. VG_(malloc)( "options.efn.3", VG_(strlen)(option_name) + VG_(strlen)(format) + 2 ); VG_(strcpy)(opt, option_name); VG_(strcat)(opt, "="); VG_(strcat)(opt, format); VG_(fmsg_bad_option)(opt, ""); } }
void VG_(split_up_argv)( Int argc, HChar** argv ) { Int i; Bool augment = True; static Bool already_called = False; XArray* /* of HChar* */ tmp_xarray; /* This function should be called once, at startup, and then never again. */ vg_assert(!already_called); already_called = True; tmp_xarray = VG_(newXA)( VG_(malloc), "commandline.sua.1", VG_(free), sizeof(HChar*) ); vg_assert( ! VG_(args_for_valgrind) ); VG_(args_for_valgrind) = VG_(newXA)( VG_(malloc), "commandline.sua.2", VG_(free), sizeof(HChar*) ); vg_assert( ! VG_(args_for_client) ); VG_(args_for_client) = VG_(newXA)( VG_(malloc), "commandline.sua.3", VG_(free), sizeof(HChar*) ); /* Collect up the args-for-V. */ i = 1; /* skip the exe (stage2) name. */ for (; i < argc; i++) { vg_assert(argv[i]); if (0 == VG_(strcmp)(argv[i], "--")) { i++; break; } if (0 == VG_(strcmp)(argv[i], "--command-line-only=yes")) augment = False; if (argv[i][0] != '-') break; add_string( tmp_xarray, argv[i] ); } /* Should now be looking at the exe name. */ if (i < argc) { vg_assert(argv[i]); VG_(args_the_exename) = argv[i]; i++; } /* The rest are args for the client. */ for (; i < argc; i++) { vg_assert(argv[i]); add_string( VG_(args_for_client), argv[i] ); } /* Get extra args from ~/.valgrindrc, $VALGRIND_OPTS and ./.valgrindrc into VG_(args_for_valgrind). */ if (augment) { // read_dot_valgrindrc() allocates the return value with // VG_(malloc)(). We do not free f1_clo and f2_clo as they get // put into VG_(args_for_valgrind) and so must persist. HChar* home = VG_(getenv)("HOME"); HChar* f1_clo = home ? read_dot_valgrindrc( home ) : NULL; HChar* env_clo = VG_(strdup)( "commandline.sua.4", VG_(getenv)(VALGRIND_OPTS) ); HChar* f2_clo = NULL; // Don't read ./.valgrindrc if "." is the same as "$HOME", else its // contents will be applied twice. (bug #142488) if (home) { const HChar *cwd = VG_(get_startup_wd)(); f2_clo = ( VG_STREQ(home, cwd) ? NULL : read_dot_valgrindrc(".") ); } if (f1_clo) add_args_from_string( f1_clo ); if (env_clo) add_args_from_string( env_clo ); if (f2_clo) add_args_from_string( f2_clo ); } /* .. and record how many extras we got. */ VG_(args_for_valgrind_noexecpass) = VG_(sizeXA)( VG_(args_for_valgrind) ); /* Finally, copy tmp_xarray onto the end. */ for (i = 0; i < VG_(sizeXA)( tmp_xarray ); i++) add_string( VG_(args_for_valgrind), * (HChar**)VG_(indexXA)( tmp_xarray, i ) ); VG_(deleteXA)( tmp_xarray ); }