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.h b/compiler/utils/assembler_test.h
index 9e23d11..9fffda5 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -53,7 +53,7 @@
typename FPReg,
typename Imm,
typename VecReg = NoVectorRegs>
-class AssemblerTest : public testing::Test {
+class AssemblerTest : public AssemblerTestBase {
public:
Ass* GetAssembler() {
return assembler_.get();
@@ -683,11 +683,6 @@
bias);
}
- // This is intended to be run as a test.
- bool CheckTools() {
- return test_helper_->CheckTools();
- }
-
// The following functions are public so that TestFn can use them...
// Returns a vector of address used by any of the repeat methods
@@ -738,23 +733,14 @@
AssemblerTest() {}
void SetUp() override {
+ AssemblerTestBase::SetUp();
allocator_.reset(new ArenaAllocator(&pool_));
assembler_.reset(CreateAssembler(allocator_.get()));
- test_helper_.reset(
- new AssemblerTestInfrastructure(GetArchitectureString(),
- GetAssemblerCmdName(),
- GetAssemblerParameters(),
- GetObjdumpCmdName(),
- GetObjdumpParameters(),
- GetDisassembleCmdName(),
- GetDisassembleParameters(),
- GetAssemblyHeader()));
-
SetUpHelpers();
}
void TearDown() override {
- test_helper_.reset(); // Clean up the helper.
+ AssemblerTestBase::TearDown();
assembler_.reset();
allocator_.reset();
}
@@ -767,38 +753,6 @@
// Override this to set up any architecture-specific things, e.g., register vectors.
virtual void SetUpHelpers() {}
- // Get the typically used name for this architecture, e.g., aarch64, x86_64, ...
- virtual std::string GetArchitectureString() = 0;
-
- // Get the name of the assembler, e.g., "as" by default.
- virtual std::string GetAssemblerCmdName() {
- return "as";
- }
-
- // Switches to the assembler command. Default none.
- virtual std::string GetAssemblerParameters() {
- return "";
- }
-
- // Get the name of the objdump, e.g., "objdump" by default.
- virtual std::string GetObjdumpCmdName() {
- return "objdump";
- }
-
- // Switches to the objdump command. Default is " -h".
- virtual std::string GetObjdumpParameters() {
- return " -h";
- }
-
- // Get the name of the objdump, e.g., "objdump" by default.
- virtual std::string GetDisassembleCmdName() {
- return "objdump";
- }
-
- // Switches to the objdump command. As it's a binary, one needs to push the architecture and
- // such to objdump, so it's architecture-specific and there is no default.
- virtual std::string GetDisassembleParameters() = 0;
-
// Create a couple of immediate values up to the number of bytes given.
virtual std::vector<int64_t> CreateImmediateValues(size_t imm_bytes, bool as_uint = false) {
std::vector<int64_t> res;
@@ -1529,11 +1483,6 @@
return sreg.str();
}
- // If the assembly file needs a header, return it in a sub-class.
- virtual const char* GetAssemblyHeader() {
- return nullptr;
- }
-
void WarnOnCombinations(size_t count) {
if (count > kWarnManyCombinationsThreshold) {
GTEST_LOG_(WARNING) << "Many combinations (" << count << "), test generation might be slow.";
@@ -1602,7 +1551,7 @@
MemoryRegion code(&(*data)[0], data->size());
assembler_->FinalizeInstructions(code);
Pad(*data);
- test_helper_->Driver(*data, assembly_text, test_name);
+ Driver(*data, assembly_text, test_name);
}
static constexpr size_t kWarnManyCombinationsThreshold = 500;
@@ -1610,7 +1559,6 @@
MallocArenaPool pool_;
std::unique_ptr<ArenaAllocator> allocator_;
std::unique_ptr<Ass> assembler_;
- std::unique_ptr<AssemblerTestInfrastructure> test_helper_;
DISALLOW_COPY_AND_ASSIGN(AssemblerTest);
};
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
diff --git a/compiler/utils/jni_macro_assembler_test.h b/compiler/utils/jni_macro_assembler_test.h
index 067a595..e77177e 100644
--- a/compiler/utils/jni_macro_assembler_test.h
+++ b/compiler/utils/jni_macro_assembler_test.h
@@ -33,7 +33,7 @@
namespace art {
template<typename Ass>
-class JNIMacroAssemblerTest : public testing::Test {
+class JNIMacroAssemblerTest : public AssemblerTestBase {
public:
Ass* GetAssembler() {
return assembler_.get();
@@ -50,32 +50,18 @@
DriverWrapper(assembly_string, test_name);
}
- // This is intended to be run as a test.
- bool CheckTools() {
- return test_helper_->CheckTools();
- }
-
protected:
JNIMacroAssemblerTest() {}
void SetUp() override {
+ AssemblerTestBase::SetUp();
allocator_.reset(new ArenaAllocator(&pool_));
assembler_.reset(CreateAssembler(allocator_.get()));
- test_helper_.reset(
- new AssemblerTestInfrastructure(GetArchitectureString(),
- GetAssemblerCmdName(),
- GetAssemblerParameters(),
- GetObjdumpCmdName(),
- GetObjdumpParameters(),
- GetDisassembleCmdName(),
- GetDisassembleParameters(),
- GetAssemblyHeader()));
-
SetUpHelpers();
}
void TearDown() override {
- test_helper_.reset(); // Clean up the helper.
+ AssemblerTestBase::TearDown();
assembler_.reset();
allocator_.reset();
}
@@ -88,43 +74,6 @@
// Override this to set up any architecture-specific things, e.g., register vectors.
virtual void SetUpHelpers() {}
- // Get the typically used name for this architecture, e.g., aarch64, x86_64, ...
- virtual std::string GetArchitectureString() = 0;
-
- // Get the name of the assembler, e.g., "as" by default.
- virtual std::string GetAssemblerCmdName() {
- return "as";
- }
-
- // Switches to the assembler command. Default none.
- virtual std::string GetAssemblerParameters() {
- return "";
- }
-
- // Get the name of the objdump, e.g., "objdump" by default.
- virtual std::string GetObjdumpCmdName() {
- return "objdump";
- }
-
- // Switches to the objdump command. Default is " -h".
- virtual std::string GetObjdumpParameters() {
- return " -h";
- }
-
- // Get the name of the objdump, e.g., "objdump" by default.
- virtual std::string GetDisassembleCmdName() {
- return "objdump";
- }
-
- // Switches to the objdump command. As it's a binary, one needs to push the architecture and
- // such to objdump, so it's architecture-specific and there is no default.
- virtual std::string GetDisassembleParameters() = 0;
-
- // If the assembly file needs a header, return it in a sub-class.
- virtual const char* GetAssemblyHeader() {
- return nullptr;
- }
-
private:
// Override this to pad the code with NOPs to a certain size if needed.
virtual void Pad(std::vector<uint8_t>& data ATTRIBUTE_UNUSED) {
@@ -137,13 +86,12 @@
MemoryRegion code(&(*data)[0], data->size());
assembler_->FinalizeInstructions(code);
Pad(*data);
- test_helper_->Driver(*data, assembly_text, test_name);
+ Driver(*data, assembly_text, test_name);
}
MallocArenaPool pool_;
std::unique_ptr<ArenaAllocator> allocator_;
std::unique_ptr<Ass> assembler_;
- std::unique_ptr<AssemblerTestInfrastructure> test_helper_;
DISALLOW_COPY_AND_ASSIGN(JNIMacroAssemblerTest);
};
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index baef254..d7bedd0 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -51,16 +51,8 @@
x86::Immediate>;
protected:
- std::string GetArchitectureString() override {
- return "x86";
- }
-
- std::string GetAssemblerParameters() override {
- return " --32";
- }
-
- std::string GetDisassembleParameters() override {
- return " -D -bbinary -mi386 --no-show-raw-insn";
+ InstructionSet GetIsa() override {
+ return InstructionSet::kX86;
}
void SetUpHelpers() override {
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index c4ca716..30f5ef2 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -144,13 +144,8 @@
x86_64::Immediate>;
protected:
- // Get the typically used name for this architecture, e.g., aarch64, x86-64, ...
- std::string GetArchitectureString() override {
- return "x86_64";
- }
-
- std::string GetDisassembleParameters() override {
- return " -D -bbinary -mi386:x86-64 -Mx86-64,addr64,data32 --no-show-raw-insn";
+ InstructionSet GetIsa() override {
+ return InstructionSet::kX86_64;
}
void SetUpHelpers() override {
@@ -2319,13 +2314,8 @@
using Base = JNIMacroAssemblerTest<x86_64::X86_64JNIMacroAssembler>;
protected:
- // Get the typically used name for this architecture, e.g., aarch64, x86-64, ...
- std::string GetArchitectureString() override {
- return "x86_64";
- }
-
- std::string GetDisassembleParameters() override {
- return " -D -bbinary -mi386:x86-64 -Mx86-64,addr64,data32 --no-show-raw-insn";
+ InstructionSet GetIsa() override {
+ return InstructionSet::kX86_64;
}
private:
diff --git a/libartbase/base/common_art_test.cc b/libartbase/base/common_art_test.cc
index e0e44a8..043d35e 100644
--- a/libartbase/base/common_art_test.cc
+++ b/libartbase/base/common_art_test.cc
@@ -53,7 +53,7 @@
using android::base::StringPrintf;
-ScratchDir::ScratchDir() {
+ScratchDir::ScratchDir(bool keep_files) : keep_files_(keep_files) {
// ANDROID_DATA needs to be set
CHECK_NE(static_cast<char*>(nullptr), getenv("ANDROID_DATA")) <<
"Are you subclassing RuntimeTest?";
@@ -65,15 +65,17 @@
}
ScratchDir::~ScratchDir() {
- // Recursively delete the directory and all its content.
- nftw(path_.c_str(), [](const char* name, const struct stat*, int type, struct FTW *) {
- if (type == FTW_F) {
- unlink(name);
- } else if (type == FTW_DP) {
- rmdir(name);
- }
- return 0;
- }, 256 /* max open file descriptors */, FTW_DEPTH);
+ if (!keep_files_) {
+ // Recursively delete the directory and all its content.
+ nftw(path_.c_str(), [](const char* name, const struct stat*, int type, struct FTW *) {
+ if (type == FTW_F) {
+ unlink(name);
+ } else if (type == FTW_DP) {
+ rmdir(name);
+ }
+ return 0;
+ }, 256 /* max open file descriptors */, FTW_DEPTH);
+ }
}
ScratchFile::ScratchFile() {
diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h
index 50aa326..fe045ca 100644
--- a/libartbase/base/common_art_test.h
+++ b/libartbase/base/common_art_test.h
@@ -45,7 +45,7 @@
class ScratchDir {
public:
- ScratchDir();
+ explicit ScratchDir(bool keep_files = false);
~ScratchDir();
@@ -55,6 +55,7 @@
private:
std::string path_;
+ bool keep_files_; // Useful for debugging.
DISALLOW_COPY_AND_ASSIGN(ScratchDir);
};
diff --git a/test/common/gtest_main.cc b/test/common/gtest_main.cc
index 9176001..e1cbdc4 100644
--- a/test/common/gtest_main.cc
+++ b/test/common/gtest_main.cc
@@ -25,8 +25,10 @@
#include "runtime.h"
extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) {
- static const char* initial_args[] = {"--deadline_threshold_ms=1200000", // hwasan takes ~10min.
- "--slow_threshold_ms=300000"};
+ static const char* initial_args[] = {
+ "--deadline_threshold_ms=1200000", // hwasan takes ~10min.
+ "--slow_threshold_ms=300000",
+ };
*args = initial_args;
*num_args = 2;
return true;