Rewrite assembler_test_base.h

Simplify the code in preparation of move to LLVM prebuilt tools.

Bug: 147817558
Test: m test-art-host-gtest
Change-Id: Iba277235255fd7d7f0965749b0b2d4a9567ced1f
diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h
index 0a7cf11..7eb3d04 100644
--- a/compiler/utils/assembler_test_base.h
+++ b/compiler/utils/assembler_test_base.h
@@ -25,452 +25,186 @@
 
 #include "android-base/strings.h"
 
+#include "base/os.h"
 #include "base/utils.h"
-#include "common_runtime_test.h"  // For ScratchFile
+#include "common_runtime_test.h"  // For ScratchDir.
+#include "elf/elf_builder.h"
+#include "elf/elf_debug_reader.h"
 #include "exec_utils.h"
+#include "stream/file_output_stream.h"
 
 namespace art {
 
-// If you want to take a look at the differences between the ART assembler and GCC, set this flag
-// to true. The disassembled files will then remain in the tmp directory.
+// Location of prebuilt tools (e.g. objdump).
+// The path needs to be updated when the prebuilt tools are updated.
+// TODO: Consider moving this logic to the build system.
+static constexpr char kPrebuiltToolsPath[] =
+    "prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/x86_64-linux-android/bin/";
+
+// If you want to take a look at the differences between the ART assembler and clang,
+// set this flag to true. The disassembled files will then remain in the tmp directory.
 static constexpr bool kKeepDisassembledFiles = false;
 
-// Use a glocal static variable to keep the same name for all test data. Else we'll just spam the
-// temp directory.
-static std::string tmpnam_;  // NOLINT [runtime/string] [4]
-
 // We put this into a class as gtests are self-contained, so this helper needs to be in an h-file.
-class AssemblerTestInfrastructure {
+class AssemblerTestBase : public testing::Test {
  public:
-  AssemblerTestInfrastructure(std::string architecture,
-                              std::string as,
-                              std::string as_params,
-                              std::string objdump,
-                              std::string objdump_params,
-                              std::string disasm,
-                              std::string disasm_params,
-                              const char* asm_header) :
-      architecture_string_(architecture),
-      asm_header_(asm_header),
-      assembler_cmd_name_(as),
-      assembler_parameters_(as_params),
-      objdump_cmd_name_(objdump),
-      objdump_parameters_(objdump_params),
-      disassembler_cmd_name_(disasm),
-      disassembler_parameters_(disasm_params) {
-    // Fake a runtime test for ScratchFile
+  AssemblerTestBase() {}
+
+  void SetUp() override {
+    // Fake a runtime test for ScratchDir.
+    CommonArtTest::SetUpAndroidRootEnvVars();
     CommonRuntimeTest::SetUpAndroidDataDir(android_data_);
+    scratch_dir_.emplace(/*keep_files=*/ kKeepDisassembledFiles);
   }
 
-  virtual ~AssemblerTestInfrastructure() {
+  void TearDown() override {
     // We leave temporaries in case this failed so we can debug issues.
     CommonRuntimeTest::TearDownAndroidDataDir(android_data_, false);
-    tmpnam_ = "";
   }
 
   // This is intended to be run as a test.
   bool CheckTools() {
-    std::string asm_tool = FindTool(assembler_cmd_name_);
-    if (!FileExists(asm_tool)) {
-      LOG(ERROR) << "Could not find assembler from " << assembler_cmd_name_;
-      LOG(ERROR) << "FindTool returned " << asm_tool;
-      FindToolDump(assembler_cmd_name_);
-      return false;
-    }
-    LOG(INFO) << "Chosen assembler command: " << GetAssemblerCommand();
-
-    std::string objdump_tool = FindTool(objdump_cmd_name_);
-    if (!FileExists(objdump_tool)) {
-      LOG(ERROR) << "Could not find objdump from " << objdump_cmd_name_;
-      LOG(ERROR) << "FindTool returned " << objdump_tool;
-      FindToolDump(objdump_cmd_name_);
-      return false;
-    }
-    LOG(INFO) << "Chosen objdump command: " << GetObjdumpCommand();
-
-    // Disassembly is optional.
-    std::string disassembler = GetDisassembleCommand();
-    if (disassembler.length() != 0) {
-      std::string disassembler_tool = FindTool(disassembler_cmd_name_);
-      if (!FileExists(disassembler_tool)) {
-        LOG(ERROR) << "Could not find disassembler from " << disassembler_cmd_name_;
-        LOG(ERROR) << "FindTool returned " << disassembler_tool;
-        FindToolDump(disassembler_cmd_name_);
+    for (auto cmd : { GetAssemblerCommand()[0], GetDisassemblerCommand()[0] }) {
+      if (!OS::FileExists(cmd.c_str())) {
+        LOG(ERROR) << "Could not find " << cmd;
         return false;
       }
-      LOG(INFO) << "Chosen disassemble command: " << GetDisassembleCommand();
-    } else {
-      LOG(INFO) << "No disassembler given.";
     }
-
     return true;
   }
 
   // Driver() assembles and compares the results. If the results are not equal and we have a
   // disassembler, disassemble both and check whether they have the same mnemonics (in which case
   // we just warn).
-  void Driver(const std::vector<uint8_t>& data,
+  void Driver(const std::vector<uint8_t>& art_code,
               const std::string& assembly_text,
               const std::string& test_name) {
-    EXPECT_NE(assembly_text.length(), 0U) << "Empty assembly";
+    ASSERT_NE(assembly_text.length(), 0U) << "Empty assembly";
+    InstructionSet isa = GetIsa();
+    auto test_path = [&](const char* ext) { return scratch_dir_->GetPath() + test_name + ext; };
 
-    NativeAssemblerResult res;
-    Compile(assembly_text, &res, test_name);
+    // Create file containing the reference source code.
+    std::string ref_asm_file = test_path(".ref.S");
+    WriteFile(ref_asm_file, assembly_text.data(), assembly_text.size());
 
-    EXPECT_TRUE(res.ok) << res.error_msg;
-    if (!res.ok) {
-      // No way of continuing.
-      return;
+    // Assemble reference object file.
+    std::string ref_obj_file = test_path(".ref.o");
+    ASSERT_TRUE(Assemble(ref_asm_file.c_str(), ref_obj_file.c_str()));
+
+    // Read the code produced by assembler from the ELF file.
+    std::vector<uint8_t> ref_code;
+    if (Is64BitInstructionSet(isa)) {
+      ReadElf</*IsElf64=*/true>(ref_obj_file, &ref_code);
+    } else {
+      ReadElf</*IsElf64=*/false>(ref_obj_file, &ref_code);
     }
 
-    if (data == *res.code) {
-      Clean(&res);
+    // Compare the ART generated code to the expected reference code.
+    if (art_code == ref_code) {
+      return;  // Success!
+    }
+
+    // Create ELF file containing the ART code.
+    std::string art_obj_file = test_path(".art.o");
+    if (Is64BitInstructionSet(isa)) {
+      WriteElf</*IsElf64=*/true>(art_obj_file, isa, art_code);
     } else {
-      if (DisassembleBinaries(data, *res.code, test_name)) {
-        if (data.size() > res.code->size()) {
-          // Fail this test with a fancy colored warning being printed.
-          EXPECT_TRUE(false) << "Assembly code is not identical, but disassembly of machine code "
-              "is equal: this implies sub-optimal encoding! Our code size=" << data.size() <<
-              ", gcc size=" << res.code->size();
-        } else {
-          // Otherwise just print an info message and clean up.
-          LOG(INFO) << "GCC chose a different encoding than ours, but the overall length is the "
-              "same.";
-          Clean(&res);
-        }
-      } else {
-        // This will output the assembly.
-        EXPECT_EQ(*res.code, data) << "Outputs (and disassembly) not identical.";
-      }
+      WriteElf</*IsElf64=*/false>(art_obj_file, isa, art_code);
+    }
+
+    // Disassemble both object files, and check that the outputs match.
+    std::string art_disassembly;
+    ASSERT_TRUE(Disassemble(art_obj_file, &art_disassembly));
+    art_disassembly = Replace(art_disassembly, art_obj_file, test_path("<extension-redacted>"));
+    std::string ref_disassembly;
+    ASSERT_TRUE(Disassemble(ref_obj_file, &ref_disassembly));
+    ref_disassembly = Replace(ref_disassembly, ref_obj_file, test_path("<extension-redacted>"));
+    ASSERT_EQ(art_disassembly, ref_disassembly) << "Outputs (and disassembly) not identical.";
+
+    // ART produced different (but valid) code than the reference assembler, report it.
+    if (art_code.size() > ref_code.size()) {
+      EXPECT_TRUE(false) << "ART code is larger then the reference code, but the disassembly"
+          "of machine code is equal: this means that ART is generating sub-optimal encoding! "
+          "ART code size=" << art_code.size() << ", reference code size=" << ref_code.size();
+    } else if (art_code.size() < ref_code.size()) {
+      EXPECT_TRUE(false) << "ART code is smaller than the reference code. Too good to be true?";
+    } else {
+      LOG(INFO) << "Reference assembler chose a different encoding than ART (of the same size)";
     }
   }
 
  protected:
-  // Return the host assembler command for this test.
-  virtual std::string GetAssemblerCommand() {
-    // Already resolved it once?
-    if (resolved_assembler_cmd_.length() != 0) {
-      return resolved_assembler_cmd_;
-    }
+  virtual InstructionSet GetIsa() = 0;
 
-    std::string line = FindTool(assembler_cmd_name_);
-    if (line.length() == 0) {
-      return line;
-    }
-
-    resolved_assembler_cmd_ = line + assembler_parameters_;
-
-    return resolved_assembler_cmd_;
+  std::string FindTool(const std::string& tool_name) {
+    return GetRootPath() + kPrebuiltToolsPath + tool_name;
   }
 
-  // Return the host objdump command for this test.
-  virtual std::string GetObjdumpCommand() {
-    // Already resolved it once?
-    if (resolved_objdump_cmd_.length() != 0) {
-      return resolved_objdump_cmd_;
-    }
-
-    std::string line = FindTool(objdump_cmd_name_);
-    if (line.length() == 0) {
-      return line;
-    }
-
-    resolved_objdump_cmd_ = line + objdump_parameters_;
-
-    return resolved_objdump_cmd_;
+  virtual std::vector<std::string> GetAssemblerCommand() {
+    return {FindTool("as"), Is64BitInstructionSet(GetIsa()) ? "--64" : "--32"};
   }
 
-  // Return the host disassembler command for this test.
-  virtual std::string GetDisassembleCommand() {
-    // Already resolved it once?
-    if (resolved_disassemble_cmd_.length() != 0) {
-      return resolved_disassemble_cmd_;
-    }
-
-    std::string line = FindTool(disassembler_cmd_name_);
-    if (line.length() == 0) {
-      return line;
-    }
-
-    resolved_disassemble_cmd_ = line + disassembler_parameters_;
-
-    return resolved_disassemble_cmd_;
+  virtual std::vector<std::string> GetDisassemblerCommand() {
+    return {FindTool("objdump"), "--disassemble", "--no-show-raw-insn"};
   }
 
  private:
-  // Structure to store intermediates and results.
-  struct NativeAssemblerResult {
-    bool ok;
-    std::string error_msg;
-    std::string base_name;
-    std::unique_ptr<std::vector<uint8_t>> code;
-    uintptr_t length;
-  };
-
-  // Compile the assembly file from_file to a binary file to_file. Returns true on success.
-  bool Assemble(const char* from_file, const char* to_file, std::string* error_msg) {
-    bool have_assembler = FileExists(FindTool(assembler_cmd_name_));
-    EXPECT_TRUE(have_assembler) << "Cannot find assembler:" << GetAssemblerCommand();
-    if (!have_assembler) {
-      return false;
-    }
-
-    std::vector<std::string> args;
-
-    // Encaspulate the whole command line in a single string passed to
-    // the shell, so that GetAssemblerCommand() may contain arguments
-    // in addition to the program name.
-    args.push_back(GetAssemblerCommand());
-    args.push_back("-o");
-    args.push_back(to_file);
-    args.push_back(from_file);
-    std::string cmd = android::base::Join(args, ' ');
-
-    args.clear();
-    args.push_back("/bin/sh");
-    args.push_back("-c");
-    args.push_back(cmd);
-
-    bool success = Exec(args, error_msg);
-    if (!success) {
-      LOG(ERROR) << "Assembler command line:";
-      for (const std::string& arg : args) {
-        LOG(ERROR) << arg;
-      }
-    }
-    return success;
+  bool Assemble(const std::string& asm_file, const std::string& obj_file) {
+    std::vector<std::string> args = GetAssemblerCommand();
+    args.insert(args.end(), {"-o", obj_file, asm_file});
+    std::string output;
+    return CommonArtTestImpl::ForkAndExec(args, [](){ return true; }, &output).StandardSuccess();
   }
 
-  // Runs objdump -h on the binary file and extracts the first line with .text.
-  // Returns "" on failure.
-  std::string Objdump(const std::string& file) {
-    bool have_objdump = FileExists(FindTool(objdump_cmd_name_));
-    EXPECT_TRUE(have_objdump) << "Cannot find objdump: " << GetObjdumpCommand();
-    if (!have_objdump) {
-      return "";
-    }
-
-    std::string error_msg;
-    std::vector<std::string> args;
-
-    // Encaspulate the whole command line in a single string passed to
-    // the shell, so that GetObjdumpCommand() may contain arguments
-    // in addition to the program name.
-    args.push_back(GetObjdumpCommand());
-    args.push_back(file);
-    args.push_back(">");
-    args.push_back(file+".dump");
-    std::string cmd = android::base::Join(args, ' ');
-
-    args.clear();
-    args.push_back("/bin/sh");
-    args.push_back("-c");
-    args.push_back(cmd);
-
-    if (!Exec(args, &error_msg)) {
-      EXPECT_TRUE(false) << error_msg;
-    }
-
-    std::ifstream dump(file+".dump");
-
-    std::string line;
-    bool found = false;
-    while (std::getline(dump, line)) {
-      if (line.find(".text") != line.npos) {
-        found = true;
-        break;
-      }
-    }
-
-    dump.close();
-
-    if (found) {
-      return line;
-    } else {
-      return "";
-    }
+  bool Disassemble(const std::string& obj_file, std::string* output) {
+    std::vector<std::string> args = GetDisassemblerCommand();
+    args.insert(args.end(), {obj_file});
+    return CommonArtTestImpl::ForkAndExec(args, [](){ return true; }, output).StandardSuccess();
   }
 
-  // Disassemble both binaries and compare the text.
-  bool DisassembleBinaries(const std::vector<uint8_t>& data,
-                           const std::vector<uint8_t>& as,
-                           const std::string& test_name) {
-    std::string disassembler = GetDisassembleCommand();
-    if (disassembler.length() == 0) {
-      LOG(WARNING) << "No dissassembler command.";
-      return false;
-    }
-
-    std::string data_name = WriteToFile(data, test_name + ".ass");
-    std::string error_msg;
-    if (!DisassembleBinary(data_name, &error_msg)) {
-      LOG(INFO) << "Error disassembling: " << error_msg;
-      std::remove(data_name.c_str());
-      return false;
-    }
-
-    std::string as_name = WriteToFile(as, test_name + ".gcc");
-    if (!DisassembleBinary(as_name, &error_msg)) {
-      LOG(INFO) << "Error disassembling: " << error_msg;
-      std::remove(data_name.c_str());
-      std::remove((data_name + ".dis").c_str());
-      std::remove(as_name.c_str());
-      return false;
-    }
-
-    bool result = CompareFiles(data_name + ".dis", as_name + ".dis");
-
-    if (!kKeepDisassembledFiles) {
-      std::remove(data_name.c_str());
-      std::remove(as_name.c_str());
-      std::remove((data_name + ".dis").c_str());
-      std::remove((as_name + ".dis").c_str());
-    }
-
-    return result;
+  std::vector<uint8_t> ReadFile(const std::string& filename) {
+    std::unique_ptr<File> file(OS::OpenFileForReading(filename.c_str()));
+    CHECK(file.get() != nullptr);
+    std::vector<uint8_t> data(file->GetLength());
+    bool success = file->ReadFully(&data[0], data.size());
+    CHECK(success) << filename;
+    return data;
   }
 
-  bool DisassembleBinary(const std::string& file, std::string* error_msg) {
-    std::vector<std::string> args;
-
-    // Encaspulate the whole command line in a single string passed to
-    // the shell, so that GetDisassembleCommand() may contain arguments
-    // in addition to the program name.
-    args.push_back(GetDisassembleCommand());
-    args.push_back(file);
-    args.push_back("| sed -n \'/<.data>/,$p\' | sed -e \'s/.*://\'");
-    args.push_back(">");
-    args.push_back(file+".dis");
-    std::string cmd = android::base::Join(args, ' ');
-
-    args.clear();
-    args.push_back("/bin/sh");
-    args.push_back("-c");
-    args.push_back(cmd);
-
-    return Exec(args, error_msg);
+  void WriteFile(const std::string& filename, const void* data, size_t size) {
+    std::unique_ptr<File> file(OS::CreateEmptyFile(filename.c_str()));
+    CHECK(file.get() != nullptr);
+    bool success = file->WriteFully(data, size);
+    CHECK(success) << filename;
+    CHECK_EQ(file->FlushClose(), 0);
   }
 
-  std::string WriteToFile(const std::vector<uint8_t>& buffer, const std::string& test_name) {
-    std::string file_name = GetTmpnam() + std::string("---") + test_name;
-    const char* data = reinterpret_cast<const char*>(buffer.data());
-    std::ofstream s_out(file_name + ".o");
-    s_out.write(data, buffer.size());
-    s_out.close();
-    return file_name + ".o";
+  // Helper method which reads the content of .text section from ELF file.
+  template<bool IsElf64>
+  void ReadElf(const std::string& filename, /*out*/ std::vector<uint8_t>* code) {
+    using ElfTypes = typename std::conditional<IsElf64, ElfTypes64, ElfTypes32>::type;
+    std::vector<uint8_t> data = ReadFile(filename);
+    ElfDebugReader<ElfTypes> reader((ArrayRef<const uint8_t>(data)));
+    const typename ElfTypes::Shdr* text = reader.GetSection(".text");
+    CHECK(text != nullptr);
+    *code = std::vector<uint8_t>(&data[text->sh_offset], &data[text->sh_offset + text->sh_size]);
   }
 
-  bool CompareFiles(const std::string& f1, const std::string& f2) {
-    std::ifstream f1_in(f1);
-    std::ifstream f2_in(f2);
-
-    bool read1_ok, read2_ok;
-    char c1, c2;
-    do {
-      read1_ok = static_cast<bool>(f1_in >> c1);
-      read2_ok = static_cast<bool>(f2_in >> c2);
-    } while (read1_ok && read2_ok && c1 == c2);
-    return !read1_ok && !read2_ok;  // Did we reach the end of both streams?
-  }
-
-  // Compile the given assembly code and extract the binary, if possible. Put result into res.
-  bool Compile(const std::string& assembly_code,
-               NativeAssemblerResult* res,
-               const std::string& test_name) {
-    res->ok = false;
-    res->code.reset(nullptr);
-
-    res->base_name = GetTmpnam() + std::string("---") + test_name;
-
-    // TODO: Lots of error checking.
-
-    std::ofstream s_out(res->base_name + ".S");
-    if (asm_header_ != nullptr) {
-      s_out << asm_header_;
-    }
-    s_out << assembly_code;
-    s_out.close();
-
-    if (!Assemble((res->base_name + ".S").c_str(), (res->base_name + ".o").c_str(),
-                  &res->error_msg)) {
-      res->error_msg = "Could not compile.";
-      return false;
-    }
-
-    std::string odump = Objdump(res->base_name + ".o");
-    if (odump.length() == 0) {
-      res->error_msg = "Objdump failed.";
-      return false;
-    }
-
-    std::istringstream iss(odump);
-    std::istream_iterator<std::string> start(iss);
-    std::istream_iterator<std::string> end;
-    std::vector<std::string> tokens(start, end);
-
-    if (tokens.size() < OBJDUMP_SECTION_LINE_MIN_TOKENS) {
-      res->error_msg = "Objdump output not recognized: too few tokens.";
-      return false;
-    }
-
-    if (tokens[1] != ".text") {
-      res->error_msg = "Objdump output not recognized: .text not second token.";
-      return false;
-    }
-
-    std::string lengthToken = "0x" + tokens[2];
-    std::istringstream(lengthToken) >> std::hex >> res->length;
-
-    std::string offsetToken = "0x" + tokens[5];
-    uintptr_t offset;
-    std::istringstream(offsetToken) >> std::hex >> offset;
-
-    std::ifstream obj(res->base_name + ".o");
-    obj.seekg(offset);
-    res->code.reset(new std::vector<uint8_t>(res->length));
-    obj.read(reinterpret_cast<char*>(&(*res->code)[0]), res->length);
-    obj.close();
-
-    res->ok = true;
-    return true;
-  }
-
-  // Remove temporary files.
-  void Clean(const NativeAssemblerResult* res) {
-    std::remove((res->base_name + ".S").c_str());
-    std::remove((res->base_name + ".o").c_str());
-    std::remove((res->base_name + ".o.dump").c_str());
-  }
-
-  // Check whether file exists. Is used for commands, so strips off any parameters: anything after
-  // the first space. We skip to the last slash for this, so it should work with directories with
-  // spaces.
-  static bool FileExists(const std::string& file) {
-    if (file.length() == 0) {
-      return false;
-    }
-
-    // Need to strip any options.
-    size_t last_slash = file.find_last_of('/');
-    if (last_slash == std::string::npos) {
-      // No slash, start looking at the start.
-      last_slash = 0;
-    }
-    size_t space_index = file.find(' ', last_slash);
-
-    if (space_index == std::string::npos) {
-      std::ifstream infile(file.c_str());
-      return infile.good();
-    } else {
-      std::string copy = file.substr(0, space_index - 1);
-
-      struct stat buf;
-      return stat(copy.c_str(), &buf) == 0;
-    }
-  }
-
-  static std::string GetGCCRootPath() {
-    return "prebuilts/gcc/linux-x86";
+  // Helper method to create an ELF file containing only the given code in the .text section.
+  template<bool IsElf64>
+  void WriteElf(const std::string& filename, InstructionSet isa, const std::vector<uint8_t>& code) {
+    using ElfTypes = typename std::conditional<IsElf64, ElfTypes64, ElfTypes32>::type;
+    std::unique_ptr<File> file(OS::CreateEmptyFile(filename.c_str()));
+    CHECK(file.get() != nullptr);
+    FileOutputStream out(file.get());
+    std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+    builder->Start(/* write_program_headers= */ false);
+    builder->GetText()->Start();
+    builder->GetText()->WriteFully(code.data(), code.size());
+    builder->GetText()->End();
+    builder->End();
+    CHECK(builder->Good());
+    CHECK_EQ(file->Close(), 0);
   }
 
   static std::string GetRootPath() {
@@ -485,127 +219,14 @@
     return getcwd(temp, 1024) ? std::string(temp) + "/" : std::string("");
   }
 
-  std::string FindTool(const std::string& tool_name) {
-    // Find the current tool. Wild-card pattern is "arch-string*tool-name".
-    std::string gcc_path = GetRootPath() + GetGCCRootPath();
-    std::vector<std::string> args;
-    args.push_back("find");
-    args.push_back(gcc_path);
-    args.push_back("-name");
-    args.push_back(architecture_string_ + "*" + tool_name);
-    args.push_back("|");
-    args.push_back("sort");
-    args.push_back("|");
-    args.push_back("tail");
-    args.push_back("-n");
-    args.push_back("1");
-    std::string tmp_file = GetTmpnam();
-    args.push_back(">");
-    args.push_back(tmp_file);
-    std::string sh_args = android::base::Join(args, ' ');
-
-    args.clear();
-    args.push_back("/bin/sh");
-    args.push_back("-c");
-    args.push_back(sh_args);
-
-    std::string error_msg;
-    if (!Exec(args, &error_msg)) {
-      EXPECT_TRUE(false) << error_msg;
-      UNREACHABLE();
-    }
-
-    std::ifstream in(tmp_file.c_str());
-    std::string line;
-    if (!std::getline(in, line)) {
-      in.close();
-      std::remove(tmp_file.c_str());
-      return "";
-    }
-    in.close();
-    std::remove(tmp_file.c_str());
-    return line;
+  std::string& Replace(std::string& str, const std::string& from, const std::string& to) {
+    auto it = str.find(from);
+    return (it != std::string::npos) ? str.replace(it, it + from.size(), to) : str;
   }
 
-  // Helper for below. If name_predicate is empty, search for all files, otherwise use it for the
-  // "-name" option.
-  static void FindToolDumpPrintout(const std::string& name_predicate,
-                                   const std::string& tmp_file) {
-    std::string gcc_path = GetRootPath() + GetGCCRootPath();
-    std::vector<std::string> args;
-    args.push_back("find");
-    args.push_back(gcc_path);
-    if (!name_predicate.empty()) {
-      args.push_back("-name");
-      args.push_back(name_predicate);
-    }
-    args.push_back("|");
-    args.push_back("sort");
-    args.push_back(">");
-    args.push_back(tmp_file);
-    std::string sh_args = android::base::Join(args, ' ');
-
-    args.clear();
-    args.push_back("/bin/sh");
-    args.push_back("-c");
-    args.push_back(sh_args);
-
-    std::string error_msg;
-    if (!Exec(args, &error_msg)) {
-      EXPECT_TRUE(false) << error_msg;
-      UNREACHABLE();
-    }
-
-    LOG(ERROR) << "FindToolDump: gcc_path=" << gcc_path
-               << " cmd=" << sh_args;
-    std::ifstream in(tmp_file.c_str());
-    if (in) {
-      std::string line;
-      while (std::getline(in, line)) {
-        LOG(ERROR) << line;
-      }
-    }
-    in.close();
-    std::remove(tmp_file.c_str());
-  }
-
-  // For debug purposes.
-  void FindToolDump(const std::string& tool_name) {
-    // Check with the tool name.
-    FindToolDumpPrintout(architecture_string_ + "*" + tool_name, GetTmpnam());
-    FindToolDumpPrintout("", GetTmpnam());
-  }
-
-  // Use a consistent tmpnam, so store it.
-  std::string GetTmpnam() {
-    if (tmpnam_.length() == 0) {
-      ScratchFile tmp;
-      tmpnam_ = tmp.GetFilename() + "asm";
-    }
-    return tmpnam_;
-  }
-
-  static constexpr size_t OBJDUMP_SECTION_LINE_MIN_TOKENS = 6;
-
-  std::string architecture_string_;
-  const char* asm_header_;
-
-  std::string assembler_cmd_name_;
-  std::string assembler_parameters_;
-
-  std::string objdump_cmd_name_;
-  std::string objdump_parameters_;
-
-  std::string disassembler_cmd_name_;
-  std::string disassembler_parameters_;
-
-  std::string resolved_assembler_cmd_;
-  std::string resolved_objdump_cmd_;
-  std::string resolved_disassemble_cmd_;
-
+  std::optional<ScratchDir> scratch_dir_;
   std::string android_data_;
-
-  DISALLOW_COPY_AND_ASSIGN(AssemblerTestInfrastructure);
+  DISALLOW_COPY_AND_ASSIGN(AssemblerTestBase);
 };
 
 }  // namespace art