diff --git a/clients/drcachesim/tests/raw2trace_unit_tests.cpp b/clients/drcachesim/tests/raw2trace_unit_tests.cpp index bb66220d8dc..417e1b75a0e 100644 --- a/clients/drcachesim/tests/raw2trace_unit_tests.cpp +++ b/clients/drcachesim/tests/raw2trace_unit_tests.cpp @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2021-2022 Google, Inc. All rights reserved. + * Copyright (c) 2021-2023 Google, Inc. All rights reserved. * **********************************************************/ /* @@ -662,7 +662,7 @@ test_chunk_boundaries(void *drcontext) // raw2trace doesn't like offsets of 0 so we shift with a nop. instr_t *nop = XINST_CREATE_nop(drcontext); // Test i#5724 where a chunk boundary between consecutive branches results - // in an incorrect count and a missing encoding entry. + // in an incorrect count. instr_t *move1 = XINST_CREATE_move(drcontext, opnd_create_reg(REG1), opnd_create_reg(REG2)); instr_t *move2 = @@ -787,13 +787,152 @@ test_chunk_boundaries(void *drcontext) check_entry(entries, idx, TRACE_TYPE_FOOTER, -1)); } +bool +test_chunk_encodings(void *drcontext) +{ + instrlist_t *ilist = instrlist_create(drcontext); + // raw2trace doesn't like offsets of 0 so we shift with a nop. + instr_t *nop = XINST_CREATE_nop(drcontext); + // Test i#5724 where a chunk boundary between consecutive branches results + // in a missing encoding entry. + instr_t *move1 = + XINST_CREATE_move(drcontext, opnd_create_reg(REG1), opnd_create_reg(REG2)); + instr_t *move2 = + XINST_CREATE_move(drcontext, opnd_create_reg(REG1), opnd_create_reg(REG2)); + instr_t *jmp2 = XINST_CREATE_jump(drcontext, opnd_create_instr(move2)); + instr_t *jmp1 = XINST_CREATE_jump(drcontext, opnd_create_instr(jmp2)); + instrlist_append(ilist, nop); + // Block 1. + instrlist_append(ilist, move1); + instrlist_append(ilist, jmp1); + // Block 2. + instrlist_append(ilist, jmp2); + // Block 3. + instrlist_append(ilist, move2); + + size_t offs_nop = 0; + size_t offs_move1 = offs_nop + instr_length(drcontext, nop); + size_t offs_jmp1 = offs_move1 + instr_length(drcontext, move1); + size_t offs_jmp2 = offs_jmp1 + instr_length(drcontext, jmp1); + size_t offs_move2 = offs_jmp2 + instr_length(drcontext, jmp2); + + // Now we synthesize our raw trace itself, including a valid header sequence. + std::vector raw; + raw.push_back(make_header()); + raw.push_back(make_tid()); + raw.push_back(make_pid()); + raw.push_back(make_line_size()); + raw.push_back(make_timestamp()); + raw.push_back(make_core()); + raw.push_back(make_block(offs_move1, 2)); + raw.push_back(make_block(offs_jmp2, 1)); + raw.push_back(make_block(offs_move2, 1)); + // Repeat the jmp,jmp to test re-emitting encodings in new chunks. + raw.push_back(make_block(offs_move1, 2)); + raw.push_back(make_block(offs_jmp2, 1)); + raw.push_back(make_block(offs_move2, 1)); + raw.push_back(make_exit()); + // We need an istream so we use istringstream. + std::ostringstream raw_out; + for (const auto &entry : raw) { + std::string as_string(reinterpret_cast(&entry), + reinterpret_cast(&entry + 1)); + raw_out << as_string; + } + std::istringstream raw_in(raw_out.str()); + std::vector input; + input.push_back(&raw_in); + // We need an archive_ostream to enable chunking. + archive_ostream_test_t result_stream; + std::vector output; + output.push_back(&result_stream); + + // Run raw2trace with our subclass supplying our decodings. + // Use a chunk instr count of 6 to split the 2nd set of 2 jumps. + raw2trace_test_t raw2trace(input, output, *ilist, drcontext, 6); + std::string error = raw2trace.do_conversion(); + CHECK(error.empty(), error); + instrlist_clear_and_destroy(drcontext, ilist); + + // Now check the results. + std::string result = result_stream.str(); + char *start = &result[0]; + char *end = start + result.size(); + CHECK(result.size() % sizeof(trace_entry_t) == 0, + "output is not a multiple of trace_entry_t"); + std::vector entries; + while (start < end) { + entries.push_back(*reinterpret_cast(start)); + start += sizeof(trace_entry_t); + } + int idx = 0; + for (const auto &entry : entries) { + std::cout << idx << " type: " << entry.type << " size: " << entry.size + << " val: " << entry.addr << "\n"; + ++idx; + } + idx = 0; + return ( + check_entry(entries, idx, TRACE_TYPE_HEADER, -1) && + check_entry(entries, idx, TRACE_TYPE_MARKER, TRACE_MARKER_TYPE_VERSION) && + check_entry(entries, idx, TRACE_TYPE_MARKER, TRACE_MARKER_TYPE_FILETYPE) && + check_entry(entries, idx, TRACE_TYPE_THREAD, -1) && + check_entry(entries, idx, TRACE_TYPE_PID, -1) && + check_entry(entries, idx, TRACE_TYPE_MARKER, TRACE_MARKER_TYPE_CACHE_LINE_SIZE) && + check_entry(entries, idx, TRACE_TYPE_MARKER, + TRACE_MARKER_TYPE_CHUNK_INSTR_COUNT) && + check_entry(entries, idx, TRACE_TYPE_MARKER, TRACE_MARKER_TYPE_TIMESTAMP) && + check_entry(entries, idx, TRACE_TYPE_MARKER, TRACE_MARKER_TYPE_CPU_ID) && + // Block 1. + check_entry(entries, idx, TRACE_TYPE_ENCODING, -1) && + check_entry(entries, idx, TRACE_TYPE_INSTR, -1) && + check_entry(entries, idx, TRACE_TYPE_ENCODING, -1) && +#ifdef X86_32 + // An extra encoding entry is needed. + check_entry(entries, idx, TRACE_TYPE_ENCODING, -1) && +#endif + check_entry(entries, idx, TRACE_TYPE_INSTR_DIRECT_JUMP, -1) && + // Block 2. + check_entry(entries, idx, TRACE_TYPE_ENCODING, -1) && +#ifdef X86_32 + // An extra encoding entry is needed. + check_entry(entries, idx, TRACE_TYPE_ENCODING, -1) && +#endif + check_entry(entries, idx, TRACE_TYPE_INSTR_DIRECT_JUMP, -1) && + // Block 3. + check_entry(entries, idx, TRACE_TYPE_ENCODING, -1) && + check_entry(entries, idx, TRACE_TYPE_INSTR, -1) && + // Now we have repeated instrs which do not need encodings, except in new chunks. + // Block 1. + check_entry(entries, idx, TRACE_TYPE_INSTR, -1) && + check_entry(entries, idx, TRACE_TYPE_INSTR_DIRECT_JUMP, -1) && + check_entry(entries, idx, TRACE_TYPE_MARKER, TRACE_MARKER_TYPE_CHUNK_FOOTER) && + // Chunk splits pair of jumps. + check_entry(entries, idx, TRACE_TYPE_MARKER, TRACE_MARKER_TYPE_RECORD_ORDINAL) && + check_entry(entries, idx, TRACE_TYPE_MARKER, TRACE_MARKER_TYPE_TIMESTAMP) && + check_entry(entries, idx, TRACE_TYPE_MARKER, TRACE_MARKER_TYPE_CPU_ID) && + // Block 2. + check_entry(entries, idx, TRACE_TYPE_ENCODING, -1) && +#ifdef X86_32 + // An extra encoding entry is needed. + check_entry(entries, idx, TRACE_TYPE_ENCODING, -1) && +#endif + check_entry(entries, idx, TRACE_TYPE_INSTR_DIRECT_JUMP, -1) && + // Block 3. + check_entry(entries, idx, TRACE_TYPE_ENCODING, -1) && + check_entry(entries, idx, TRACE_TYPE_INSTR, -1) && + check_entry(entries, idx, TRACE_TYPE_THREAD_EXIT, -1) && + check_entry(entries, idx, TRACE_TYPE_FOOTER, -1)); +} + int main(int argc, const char *argv[]) { void *drcontext = dr_standalone_init(); if (!test_branch_delays(drcontext) || !test_marker_placement(drcontext) || - !test_marker_delays(drcontext) || !test_chunk_boundaries(drcontext)) + !test_marker_delays(drcontext) || !test_chunk_boundaries(drcontext) || + !test_chunk_encodings(drcontext)) return 1; return 0; } diff --git a/clients/drcachesim/tracer/raw2trace.cpp b/clients/drcachesim/tracer/raw2trace.cpp index d3993c8667b..dd88c7a2db5 100644 --- a/clients/drcachesim/tracer/raw2trace.cpp +++ b/clients/drcachesim/tracer/raw2trace.cpp @@ -1135,10 +1135,15 @@ raw2trace_t::append_delayed_branch(void *tls) if (tdata->delayed_branch.empty()) return ""; if (verbosity_ >= 4) { - for (const auto &entry : tdata->delayed_branch) { + int instr_count = 0; + for (size_t i = 0; i < tdata->delayed_branch.size(); i++) { + const auto &entry = tdata->delayed_branch[i]; if (type_is_instr(static_cast(entry.type))) { - VPRINT(4, "Appending delayed branch pc=" PIFX " for thread %d\n", - entry.addr, tdata->index); + VPRINT(4, + "Appending delayed branch pc=" PIFX " decode=%p for thread %d\n", + entry.addr, tdata->delayed_branch_decode_pcs[instr_count], + tdata->index); + ++instr_count; } else { VPRINT(4, "Appending delayed branch tagalong entry type %s (%d) for thread " @@ -1147,12 +1152,13 @@ raw2trace_t::append_delayed_branch(void *tls) } } } - std::string error = - write(tdata, tdata->delayed_branch.data(), - tdata->delayed_branch.data() + tdata->delayed_branch.size()); + std::string error = write(tdata, tdata->delayed_branch.data(), + tdata->delayed_branch.data() + tdata->delayed_branch.size(), + tdata->delayed_branch_decode_pcs); if (!error.empty()) return error; tdata->delayed_branch.clear(); + tdata->delayed_branch_decode_pcs.clear(); return ""; } @@ -1249,38 +1255,77 @@ raw2trace_t::open_new_chunk(raw2trace_thread_data_t *tdata) return ""; } +std::string +raw2trace_t::append_encoding(void *tls, app_pc pc, size_t instr_length, + trace_entry_t *&buf, trace_entry_t *buf_start) +{ + size_t size_left = instr_length; + size_t offs = 0; +#ifdef ARM + // Remove any Thumb LSB. + pc = dr_app_pc_as_load_target(DR_ISA_ARM_THUMB, pc); +#endif + do { + buf->type = TRACE_TYPE_ENCODING; + buf->size = + static_cast(std::min(size_left, sizeof(buf->encoding))); + memcpy(buf->encoding, pc + offs, buf->size); + if (buf->size < sizeof(buf->encoding)) { + // We don't have to set the rest to 0 but it is nice. + memset(buf->encoding + buf->size, 0, sizeof(buf->encoding) - buf->size); + } + log(4, "Appended encoding entry for %p sz=%zu 0x%08x...\n", pc, buf->size, + *(int *)buf->encoding); + offs += buf->size; + size_left -= buf->size; + ++buf; + CHECK(static_cast(buf - buf_start) < WRITE_BUFFER_SIZE, + "Too many entries for write buffer"); + } while (size_left > 0); + return ""; +} + +std::string +raw2trace_t::insert_post_chunk_encodings(void *tls, const trace_entry_t *instr, + app_pc decode_pc) +{ + auto tdata = reinterpret_cast(tls); + trace_entry_t encodings[WRITE_BUFFER_SIZE]; + trace_entry_t *buf = encodings; + log(4, "Adding post-chunk-boundary encoding entry for decode=%p app=%p\n", decode_pc, + instr->addr); + std::string err = append_encoding(tls, decode_pc, instr->size, buf, encodings); + if (!err.empty()) + return err; + if (!tdata->out_file->write(reinterpret_cast(encodings), + reinterpret_cast(buf) - + reinterpret_cast(encodings))) + return "Failed to write to output file"; + return ""; +} + // All writes to out_file go through this function, except new chunk headers // and footers (to do so would cause recursion; we assume those do not need // extra processing here). std::string -raw2trace_t::write(void *tls, const trace_entry_t *start, const trace_entry_t *end) +raw2trace_t::write(void *tls, const trace_entry_t *start, const trace_entry_t *end, + std::vector decode_pcs) { + if (end == start) + return "Empty buffer passed to write()"; auto tdata = reinterpret_cast(tls); if (tdata->out_archive != nullptr) { + bool prev_was_encoding = false; + int instr_ordinal = -1; for (const trace_entry_t *it = start; it < end; ++it) { tdata->cur_chunk_ref_count += tdata->memref_counter.entry_memref_count(it); - if (it->type == TRACE_TYPE_MARKER) { - if (it->size == TRACE_MARKER_TYPE_TIMESTAMP) - tdata->last_timestamp_ = it->addr; - else if (it->size == TRACE_MARKER_TYPE_CPU_ID) { - tdata->last_cpu_ = static_cast(it->addr); - tdata->sched.emplace_back(tdata->tid, tdata->last_timestamp_, - tdata->last_cpu_, - tdata->cur_chunk_instr_count); - tdata->cpu2sched[it->addr].emplace_back( - tdata->tid, tdata->last_timestamp_, tdata->last_cpu_, - tdata->cur_chunk_instr_count); - } - continue; - } // We wait until we're past the final instr to write, to ensure we // get all its memrefs, by not stopping until we hit an instr or an // encoding. (We will put function markers for entry in the // prior chunk too: we live with that.) - if (!type_is_instr(static_cast(it->type)) && - it->type != TRACE_TYPE_ENCODING) - continue; - if (tdata->cur_chunk_instr_count >= chunk_instr_count_) { + if ((type_is_instr(static_cast(it->type)) || + it->type == TRACE_TYPE_ENCODING) && + tdata->cur_chunk_instr_count >= chunk_instr_count_) { DEBUG_ASSERT(tdata->cur_chunk_instr_count == chunk_instr_count_); if (!tdata->out_file->write(reinterpret_cast(start), reinterpret_cast(it) - @@ -1291,15 +1336,66 @@ raw2trace_t::write(void *tls, const trace_entry_t *start, const trace_entry_t *e return error; start = it; DEBUG_ASSERT(tdata->cur_chunk_instr_count == 0); - // TODO i#5724: We need to re-emit encodings for "it" and any further - // instrs in this buffer: have a callback passed in which constructs - // an encoding from an instr record? } - if (type_is_instr(static_cast(it->type))) + if (type_is_instr(static_cast(it->type))) { ++tdata->cur_chunk_instr_count; + ++instr_ordinal; + if (TESTANY(OFFLINE_FILE_TYPE_ENCODINGS, tdata->file_type) && + // We don't want encodings for the PC-only i-filtered entries. + it->size > 0 && instr_ordinal >= static_cast(decode_pcs.size())) + return "decode_pcs is missing entries for written instructions"; + } + // Check for missing encodings after possibly opening a new chunk. + // There can be multiple delayed branches in the same buffer here + // so multiple could appear on the other side of a new chunk. + // + // XXX i#5724: Could we add a trace_entry_t-level invariant checker to + // identify missing post-chunk encodings? Or should we have the reader + // deliberately clear its encoding history on a chunk boundary, raising + // a fatal error on a missing encoding? For now the only complex case + // is these already-generated records which we handle here and have a + // unit test covering so those further checks are lower priority. + if (TESTANY(OFFLINE_FILE_TYPE_ENCODINGS, tdata->file_type) && + type_is_instr(static_cast(it->type)) && + // We don't want encodings for the PC-only i-filtered entries. + it->size > 0 && !prev_was_encoding && + record_encoding_emitted(tls, decode_pcs[instr_ordinal])) { + // Write any data we were waiting until post-loop to write. + if (it > start && + !tdata->out_file->write(reinterpret_cast(start), + reinterpret_cast(it) - + reinterpret_cast(start))) + return "Failed to write to output file"; + std::string err = + insert_post_chunk_encodings(tls, it, decode_pcs[instr_ordinal]); + if (!err.empty()) + return err; + if (!tdata->out_file->write(reinterpret_cast(it), + sizeof(*it))) + return "Failed to write to output file"; + start = it + 1; + } + if (it->type == TRACE_TYPE_ENCODING) + prev_was_encoding = true; + else + prev_was_encoding = false; + if (it->type == TRACE_TYPE_MARKER) { + if (it->size == TRACE_MARKER_TYPE_TIMESTAMP) + tdata->last_timestamp_ = it->addr; + else if (it->size == TRACE_MARKER_TYPE_CPU_ID) { + tdata->last_cpu_ = static_cast(it->addr); + tdata->sched.emplace_back(tdata->tid, tdata->last_timestamp_, + tdata->last_cpu_, + tdata->cur_chunk_instr_count); + tdata->cpu2sched[it->addr].emplace_back( + tdata->tid, tdata->last_timestamp_, tdata->last_cpu_, + tdata->cur_chunk_instr_count); + } + } } } - if (!tdata->out_file->write(reinterpret_cast(start), + if (end > start && + !tdata->out_file->write(reinterpret_cast(start), reinterpret_cast(end) - reinterpret_cast(start))) return "Failed to write to output file"; @@ -1316,11 +1412,23 @@ raw2trace_t::write(void *tls, const trace_entry_t *start, const trace_entry_t *e std::string raw2trace_t::write_delayed_branches(void *tls, const trace_entry_t *start, - const trace_entry_t *end) + const trace_entry_t *end, app_pc decode_pc) { auto tdata = reinterpret_cast(tls); - for (const trace_entry_t *it = start; it < end; ++it) + int instr_count = 0; + for (const trace_entry_t *it = start; it < end; ++it) { tdata->delayed_branch.push_back(*it); + if (type_is_instr(static_cast(it->type))) + ++instr_count; + } + if (instr_count > 1) + return "Only one instruction per delayed branch bundle is supported"; + if (instr_count == 1) { + if (decode_pc == nullptr) + return "A delayed instruction must have a valid decode PC"; + tdata->delayed_branch_decode_pcs.push_back(decode_pc); + } else if (decode_pc != nullptr) + return "Delayed non-instructions should not have a decode PC"; return ""; } diff --git a/clients/drcachesim/tracer/raw2trace.h b/clients/drcachesim/tracer/raw2trace.h index 06d2d761bd5..00a0313d75d 100644 --- a/clients/drcachesim/tracer/raw2trace.h +++ b/clients/drcachesim/tracer/raw2trace.h @@ -649,18 +649,21 @@ struct trace_header_t { * get_write_buffer() may reuse the same buffer after write() or write_delayed_branches() * is called. * - *
  • std::string write(void *tls, const trace_entry_t *start, const trace_entry_t *end) + *
  • std::string write(void *tls, const trace_entry_t *start, const trace_entry_t *end, + * std::vector decode_pcs = {}) * * Writes the converted traces between start and end, where end is past the last * item to write. Both start and end are assumed to be pointers inside a buffer - * returned by get_write_buffer().
  • + * returned by get_write_buffer(). decode_pcs is only needed if there is more + * than one instruction in the buffer; in that case it must contain one entry per + * instruction. * *
  • std::string write_delayed_branches(void *tls, const trace_entry_t *start, - * const trace_entry_t *end) + * const trace_entry_t *end, app_pc decode_pc) * * Similar to write(), but treat the provided traces as delayed branches: if they * are the last values in a record, they belong to the next record of the same - * thread.
  • + * thread. The start..end sequence must contain one instruction. * *
  • bool delayed_branches_exist(void *tls) * @@ -752,6 +755,11 @@ struct trace_header_t { * * Returns the trace file type (a combination of OFFLINE_FILE_TYPE* constants). *
  • + * + *
  • std::string append_encoding(void *tls, app_pc pc, size_t instr_length, + * trace_entry_t *&buf, trace_entry_t *buf_start) + * + * Writes encoding entries for pc..pc+instr_length to buf. * */ template class trace_converter_t { @@ -1236,6 +1244,7 @@ template class trace_converter_t { for (uint i = 0; i < instr_count; ++i) { trace_entry_t *buf_start = impl()->get_write_buffer(tls); trace_entry_t *buf = buf_start; + app_pc saved_decode_pc = decode_pc; app_pc orig_pc = modmap_().get_orig_pc_from_map_pc( decode_pc, in_entry->pc.modidx, in_entry->pc.modoffs); // To avoid repeatedly decoding the same instruction on every one of its @@ -1259,7 +1268,8 @@ template class trace_converter_t { return error; } if (!skip_icache && impl()->record_encoding_emitted(tls, decode_pc)) { - error = append_encoding(instr, buf, buf_start, decode_pc); + error = impl()->append_encoding(tls, decode_pc, instr->length(), buf, + buf_start); if (!error.empty()) return error; } @@ -1372,12 +1382,14 @@ template class trace_converter_t { // timestamp of the branch, which we live with. To avoid marker // misplacement (e.g. in the middle of a basic block), we also // delay markers. - impl()->log(4, "Delaying %d entries\n", buf - buf_start); - error = impl()->write_delayed_branches(tls, buf_start, buf); + impl()->log(4, "Delaying %d entries for decode=" PIFX "\n", + buf - buf_start, saved_decode_pc); + error = + impl()->write_delayed_branches(tls, buf_start, buf, saved_decode_pc); if (!error.empty()) return error; } else { - error = impl()->write(tls, buf_start, buf); + error = impl()->write(tls, buf_start, buf, { saved_decode_pc }); if (!error.empty()) return error; } @@ -1388,36 +1400,6 @@ template class trace_converter_t { return ""; } - std::string - append_encoding(const instr_summary_t *instr, trace_entry_t *&buf, - trace_entry_t *buf_start, app_pc pc) - { - size_t size_left = instr->length(); - size_t offs = 0; -#ifdef ARM - // Remove any Thumb LSB. - pc = dr_app_pc_as_load_target(DR_ISA_ARM_THUMB, pc); -#endif - do { - buf->type = TRACE_TYPE_ENCODING; - buf->size = - static_cast(std::min(size_left, sizeof(buf->encoding))); - memcpy(buf->encoding, pc + offs, buf->size); - if (buf->size < sizeof(buf->encoding)) { - // We don't have to set the rest to 0 but it is nice. - memset(buf->encoding + buf->size, 0, sizeof(buf->encoding) - buf->size); - } - impl()->log(4, "Appended encoding entry for %p sz=%zu 0x%08x...\n", pc, - buf->size, *(int *)buf->encoding); - offs += buf->size; - size_left -= buf->size; - ++buf; - DR_CHECK(static_cast(buf - buf_start) < WRITE_BUFFER_SIZE, - "Too many entries for write buffer"); - } while (size_left > 0); - return ""; - } - // Returns true if a kernel interrupt happened at cur_pc. // Outputs a kernel interrupt if this is the right location. // Outputs any other markers observed if !instrs_are_separate, since they @@ -1957,6 +1939,9 @@ class raw2trace_t : public trace_converter_t { // Used to delay a thread-buffer-final branch to keep it next to its target. std::vector delayed_branch; + // Records the decode pcs for delayed_branch instructions for re-inserting + // encodings across a chunk boundary. + std::vector delayed_branch_decode_pcs; // Current trace conversion state. bool saw_header; @@ -2030,10 +2015,17 @@ class raw2trace_t : public trace_converter_t { trace_entry_t * get_write_buffer(void *tls); std::string - write(void *tls, const trace_entry_t *start, const trace_entry_t *end); + write(void *tls, const trace_entry_t *start, const trace_entry_t *end, + std::vector decode_pcs = {}); std::string write_delayed_branches(void *tls, const trace_entry_t *start, - const trace_entry_t *end); + const trace_entry_t *end, app_pc decode_pc = nullptr); + std::string + append_encoding(void *tls, app_pc pc, size_t instr_length, trace_entry_t *&buf, + trace_entry_t *buf_start); + std::string + insert_post_chunk_encodings(void *tls, const trace_entry_t *instr, app_pc decode_pc); + bool delayed_branches_exist(void *tls); bool diff --git a/suite/runsuite_wrapper.pl b/suite/runsuite_wrapper.pl index aee2ed48817..82b55ecb37e 100755 --- a/suite/runsuite_wrapper.pl +++ b/suite/runsuite_wrapper.pl @@ -1,7 +1,7 @@ #!/usr/bin/perl # ********************************************************** -# Copyright () 2016-2022 Google, Inc. All rights reserved. +# Copyright () 2016-2023 Google, Inc. All rights reserved. # ********************************************************** # Redistribution and use in source and binary forms, with or without @@ -341,7 +341,6 @@ 'code_api,tracedump_text,tracedump_origins,syntax_intel|common.loglevel' => 1, # i#1807 'code_api|client.attach_test' => 1, # i#5740 'code_api|client.attach_blocking' => 1, # i#5740 - 'code_api|tool.drcacheoff.invariant_checker' => 1, # i#5724 'code_api|tool.drcacheoff.rseq' => 1, # i#5734 'code_api|tool.drcacheoff.windows-zlib' => 1, # i#5507 );