blob: 0378176afd1f23b59690396e40756eaf9889fe31 [file] [log] [blame]
Andreas Gampe5a4fa822014-03-31 16:50:12 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ART_COMPILER_UTILS_ASSEMBLER_TEST_H_
18#define ART_COMPILER_UTILS_ASSEMBLER_TEST_H_
19
20#include "assembler.h"
21
Andreas Gampeb40c6a72014-05-02 14:25:12 -070022#include "common_runtime_test.h" // For ScratchFile
Andreas Gampe5a4fa822014-03-31 16:50:12 -070023
24#include <cstdio>
25#include <cstdlib>
26#include <fstream>
Andreas Gampe5a4fa822014-03-31 16:50:12 -070027#include <iterator>
28#include <sys/stat.h>
29
30namespace art {
31
Andreas Gampeb40c6a72014-05-02 14:25:12 -070032// Use a glocal static variable to keep the same name for all test data. Else we'll just spam the
33// temp directory.
34static std::string tmpnam_;
35
Andreas Gampe5a4fa822014-03-31 16:50:12 -070036template<typename Ass, typename Reg, typename Imm>
37class AssemblerTest : public testing::Test {
38 public:
39 Ass* GetAssembler() {
40 return assembler_.get();
41 }
42
43 typedef std::string (*TestFn)(Ass* assembler);
44
45 void DriverFn(TestFn f, std::string test_name) {
46 Driver(f(assembler_.get()), test_name);
47 }
48
49 // This driver assumes the assembler has already been called.
50 void DriverStr(std::string assembly_string, std::string test_name) {
51 Driver(assembly_string, test_name);
52 }
53
54 std::string RepeatR(void (Ass::*f)(Reg), std::string fmt) {
55 const std::vector<Reg*> registers = GetRegisters();
56 std::string str;
57 for (auto reg : registers) {
58 (assembler_.get()->*f)(*reg);
59 std::string base = fmt;
60
61 size_t reg_index = base.find("{reg}");
62 if (reg_index != std::string::npos) {
63 std::ostringstream sreg;
64 sreg << *reg;
65 std::string reg_string = sreg.str();
66 base.replace(reg_index, 5, reg_string);
67 }
68
69 if (str.size() > 0) {
70 str += "\n";
71 }
72 str += base;
73 }
74 // Add a newline at the end.
75 str += "\n";
76 return str;
77 }
78
79 std::string RepeatRR(void (Ass::*f)(Reg, Reg), std::string fmt) {
80 const std::vector<Reg*> registers = GetRegisters();
81 std::string str;
82 for (auto reg1 : registers) {
83 for (auto reg2 : registers) {
84 (assembler_.get()->*f)(*reg1, *reg2);
85 std::string base = fmt;
86
87 size_t reg1_index = base.find("{reg1}");
88 if (reg1_index != std::string::npos) {
89 std::ostringstream sreg;
90 sreg << *reg1;
91 std::string reg_string = sreg.str();
92 base.replace(reg1_index, 6, reg_string);
93 }
94
95 size_t reg2_index = base.find("{reg2}");
96 if (reg2_index != std::string::npos) {
97 std::ostringstream sreg;
98 sreg << *reg2;
99 std::string reg_string = sreg.str();
100 base.replace(reg2_index, 6, reg_string);
101 }
102
103 if (str.size() > 0) {
104 str += "\n";
105 }
106 str += base;
107 }
108 }
109 // Add a newline at the end.
110 str += "\n";
111 return str;
112 }
113
114 std::string RepeatRI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) {
115 const std::vector<Reg*> registers = GetRegisters();
116 std::string str;
117 std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
118 for (auto reg : registers) {
119 for (int64_t imm : imms) {
Ian Rogerscf7f1912014-10-22 22:06:39 -0700120 Imm new_imm = CreateImmediate(imm);
121 (assembler_.get()->*f)(*reg, new_imm);
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700122 std::string base = fmt;
123
124 size_t reg_index = base.find("{reg}");
125 if (reg_index != std::string::npos) {
126 std::ostringstream sreg;
127 sreg << *reg;
128 std::string reg_string = sreg.str();
129 base.replace(reg_index, 5, reg_string);
130 }
131
132 size_t imm_index = base.find("{imm}");
133 if (imm_index != std::string::npos) {
134 std::ostringstream sreg;
135 sreg << imm;
136 std::string imm_string = sreg.str();
137 base.replace(imm_index, 5, imm_string);
138 }
139
140 if (str.size() > 0) {
141 str += "\n";
142 }
143 str += base;
144 }
145 }
146 // Add a newline at the end.
147 str += "\n";
148 return str;
149 }
150
151 std::string RepeatI(void (Ass::*f)(const Imm&), size_t imm_bytes, std::string fmt) {
152 std::string str;
153 std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
154 for (int64_t imm : imms) {
Ian Rogerscf7f1912014-10-22 22:06:39 -0700155 Imm new_imm = CreateImmediate(imm);
156 (assembler_.get()->*f)(new_imm);
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700157 std::string base = fmt;
158
159 size_t imm_index = base.find("{imm}");
160 if (imm_index != std::string::npos) {
161 std::ostringstream sreg;
162 sreg << imm;
163 std::string imm_string = sreg.str();
164 base.replace(imm_index, 5, imm_string);
165 }
166
167 if (str.size() > 0) {
168 str += "\n";
169 }
170 str += base;
171 }
172 // Add a newline at the end.
173 str += "\n";
174 return str;
175 }
176
177 // This is intended to be run as a test.
178 bool CheckTools() {
179 if (!FileExists(GetAssemblerCommand())) {
180 return false;
181 }
182 LOG(INFO) << "Chosen assembler command: " << GetAssemblerCommand();
183
184 if (!FileExists(GetObjdumpCommand())) {
185 return false;
186 }
187 LOG(INFO) << "Chosen objdump command: " << GetObjdumpCommand();
188
189 // Disassembly is optional.
190 std::string disassembler = GetDisassembleCommand();
191 if (disassembler.length() != 0) {
192 if (!FileExists(disassembler)) {
193 return false;
194 }
195 LOG(INFO) << "Chosen disassemble command: " << GetDisassembleCommand();
196 } else {
197 LOG(INFO) << "No disassembler given.";
198 }
199
200 return true;
201 }
202
203 protected:
204 void SetUp() OVERRIDE {
205 assembler_.reset(new Ass());
206
Andreas Gampeb40c6a72014-05-02 14:25:12 -0700207 // Fake a runtime test for ScratchFile
Andreas Gampe7747c8d2014-08-06 14:53:03 -0700208 CommonRuntimeTest::SetUpAndroidData(android_data_);
Andreas Gampeb40c6a72014-05-02 14:25:12 -0700209
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700210 SetUpHelpers();
211 }
212
Andreas Gampe7747c8d2014-08-06 14:53:03 -0700213 void TearDown() OVERRIDE {
214 // We leave temporaries in case this failed so we can debug issues.
215 CommonRuntimeTest::TearDownAndroidData(android_data_, false);
216 tmpnam_ = "";
217 }
218
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700219 // Override this to set up any architecture-specific things, e.g., register vectors.
220 virtual void SetUpHelpers() {}
221
222 virtual std::vector<Reg*> GetRegisters() = 0;
223
224 // Get the typically used name for this architecture, e.g., aarch64, x86_64, ...
225 virtual std::string GetArchitectureString() = 0;
226
227 // Get the name of the assembler, e.g., "as" by default.
228 virtual std::string GetAssemblerCmdName() {
229 return "as";
230 }
231
232 // Switches to the assembler command. Default none.
233 virtual std::string GetAssemblerParameters() {
234 return "";
235 }
236
237 // Return the host assembler command for this test.
238 virtual std::string GetAssemblerCommand() {
239 // Already resolved it once?
240 if (resolved_assembler_cmd_.length() != 0) {
241 return resolved_assembler_cmd_;
242 }
243
244 std::string line = FindTool(GetAssemblerCmdName());
245 if (line.length() == 0) {
246 return line;
247 }
248
249 resolved_assembler_cmd_ = line + GetAssemblerParameters();
250
251 return line;
252 }
253
254 // Get the name of the objdump, e.g., "objdump" by default.
255 virtual std::string GetObjdumpCmdName() {
256 return "objdump";
257 }
258
259 // Switches to the objdump command. Default is " -h".
260 virtual std::string GetObjdumpParameters() {
261 return " -h";
262 }
263
264 // Return the host objdump command for this test.
265 virtual std::string GetObjdumpCommand() {
266 // Already resolved it once?
267 if (resolved_objdump_cmd_.length() != 0) {
268 return resolved_objdump_cmd_;
269 }
270
271 std::string line = FindTool(GetObjdumpCmdName());
272 if (line.length() == 0) {
273 return line;
274 }
275
276 resolved_objdump_cmd_ = line + GetObjdumpParameters();
277
278 return line;
279 }
280
281 // Get the name of the objdump, e.g., "objdump" by default.
282 virtual std::string GetDisassembleCmdName() {
283 return "objdump";
284 }
285
286 // Switches to the objdump command. As it's a binary, one needs to push the architecture and
287 // such to objdump, so it's architecture-specific and there is no default.
288 virtual std::string GetDisassembleParameters() = 0;
289
290 // Return the host disassembler command for this test.
291 virtual std::string GetDisassembleCommand() {
292 // Already resolved it once?
293 if (resolved_disassemble_cmd_.length() != 0) {
294 return resolved_disassemble_cmd_;
295 }
296
297 std::string line = FindTool(GetDisassembleCmdName());
298 if (line.length() == 0) {
299 return line;
300 }
301
302 resolved_disassemble_cmd_ = line + GetDisassembleParameters();
303
304 return line;
305 }
306
307 // Create a couple of immediate values up to the number of bytes given.
308 virtual std::vector<int64_t> CreateImmediateValues(size_t imm_bytes) {
309 std::vector<int64_t> res;
310 res.push_back(0);
311 res.push_back(-1);
312 res.push_back(0x12);
313 if (imm_bytes >= 2) {
314 res.push_back(0x1234);
315 res.push_back(-0x1234);
316 if (imm_bytes >= 4) {
317 res.push_back(0x12345678);
318 res.push_back(-0x12345678);
319 if (imm_bytes >= 6) {
320 res.push_back(0x123456789ABC);
321 res.push_back(-0x123456789ABC);
322 if (imm_bytes >= 8) {
323 res.push_back(0x123456789ABCDEF0);
324 res.push_back(-0x123456789ABCDEF0);
325 }
326 }
327 }
328 }
329 return res;
330 }
331
332 // Create an immediate from the specific value.
Ian Rogerscf7f1912014-10-22 22:06:39 -0700333 virtual Imm CreateImmediate(int64_t imm_value) = 0;
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700334
335 private:
336 // Driver() assembles and compares the results. If the results are not equal and we have a
337 // disassembler, disassemble both and check whether they have the same mnemonics (in which case
338 // we just warn).
339 void Driver(std::string assembly_text, std::string test_name) {
340 EXPECT_NE(assembly_text.length(), 0U) << "Empty assembly";
341
342 NativeAssemblerResult res;
343 Compile(assembly_text, &res, test_name);
344
345 EXPECT_TRUE(res.ok) << res.error_msg;
346 if (!res.ok) {
347 // No way of continuing.
348 return;
349 }
350
351 size_t cs = assembler_->CodeSize();
Ian Rogers700a4022014-05-19 16:49:03 -0700352 std::unique_ptr<std::vector<uint8_t>> data(new std::vector<uint8_t>(cs));
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700353 MemoryRegion code(&(*data)[0], data->size());
354 assembler_->FinalizeInstructions(code);
355
356 if (*data == *res.code) {
357 Clean(&res);
358 } else {
359 if (DisassembleBinaries(*data, *res.code, test_name)) {
360 if (data->size() > res.code->size()) {
Andreas Gampe54e15de2014-08-06 15:31:06 -0700361 // Fail this test with a fancy colored warning being printed.
362 EXPECT_TRUE(false) << "Assembly code is not identical, but disassembly of machine code "
363 "is equal: this implies sub-optimal encoding! Our code size=" << data->size() <<
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700364 ", gcc size=" << res.code->size();
365 } else {
Andreas Gampe54e15de2014-08-06 15:31:06 -0700366 // Otherwise just print an info message and clean up.
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700367 LOG(INFO) << "GCC chose a different encoding than ours, but the overall length is the "
368 "same.";
Andreas Gampe54e15de2014-08-06 15:31:06 -0700369 Clean(&res);
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700370 }
371 } else {
372 // This will output the assembly.
Nicolas Geoffray102cbed2014-10-15 18:31:05 +0100373 EXPECT_EQ(*res.code, *data) << "Outputs (and disassembly) not identical.";
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700374 }
375 }
376 }
377
378 // Structure to store intermediates and results.
379 struct NativeAssemblerResult {
380 bool ok;
381 std::string error_msg;
382 std::string base_name;
Ian Rogers700a4022014-05-19 16:49:03 -0700383 std::unique_ptr<std::vector<uint8_t>> code;
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700384 uintptr_t length;
385 };
386
387 // Compile the assembly file from_file to a binary file to_file. Returns true on success.
388 bool Assemble(const char* from_file, const char* to_file, std::string* error_msg) {
389 bool have_assembler = FileExists(GetAssemblerCommand());
390 EXPECT_TRUE(have_assembler) << "Cannot find assembler:" << GetAssemblerCommand();
391 if (!have_assembler) {
392 return false;
393 }
394
395 std::vector<std::string> args;
396
Roland Levillain1a28fc42014-11-13 18:03:06 +0000397 // Encaspulate the whole command line in a single string passed to
398 // the shell, so that GetAssemblerCommand() may contain arguments
399 // in addition to the program name.
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700400 args.push_back(GetAssemblerCommand());
401 args.push_back("-o");
402 args.push_back(to_file);
403 args.push_back(from_file);
Roland Levillain1a28fc42014-11-13 18:03:06 +0000404 std::string cmd = Join(args, ' ');
405
406 args.clear();
407 args.push_back("/bin/sh");
408 args.push_back("-c");
409 args.push_back(cmd);
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700410
411 return Exec(args, error_msg);
412 }
413
414 // Runs objdump -h on the binary file and extracts the first line with .text.
415 // Returns "" on failure.
416 std::string Objdump(std::string file) {
417 bool have_objdump = FileExists(GetObjdumpCommand());
418 EXPECT_TRUE(have_objdump) << "Cannot find objdump: " << GetObjdumpCommand();
419 if (!have_objdump) {
420 return "";
421 }
422
423 std::string error_msg;
424 std::vector<std::string> args;
425
Roland Levillain1a28fc42014-11-13 18:03:06 +0000426 // Encaspulate the whole command line in a single string passed to
427 // the shell, so that GetObjdumpCommand() may contain arguments
428 // in addition to the program name.
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700429 args.push_back(GetObjdumpCommand());
430 args.push_back(file);
431 args.push_back(">");
432 args.push_back(file+".dump");
433 std::string cmd = Join(args, ' ');
434
435 args.clear();
436 args.push_back("/bin/sh");
437 args.push_back("-c");
438 args.push_back(cmd);
439
440 if (!Exec(args, &error_msg)) {
441 EXPECT_TRUE(false) << error_msg;
442 }
443
444 std::ifstream dump(file+".dump");
445
446 std::string line;
447 bool found = false;
448 while (std::getline(dump, line)) {
449 if (line.find(".text") != line.npos) {
450 found = true;
451 break;
452 }
453 }
454
455 dump.close();
456
457 if (found) {
458 return line;
459 } else {
460 return "";
461 }
462 }
463
464 // Disassemble both binaries and compare the text.
465 bool DisassembleBinaries(std::vector<uint8_t>& data, std::vector<uint8_t>& as,
466 std::string test_name) {
467 std::string disassembler = GetDisassembleCommand();
468 if (disassembler.length() == 0) {
469 LOG(WARNING) << "No dissassembler command.";
470 return false;
471 }
472
473 std::string data_name = WriteToFile(data, test_name + ".ass");
474 std::string error_msg;
475 if (!DisassembleBinary(data_name, &error_msg)) {
476 LOG(INFO) << "Error disassembling: " << error_msg;
477 std::remove(data_name.c_str());
478 return false;
479 }
480
481 std::string as_name = WriteToFile(as, test_name + ".gcc");
482 if (!DisassembleBinary(as_name, &error_msg)) {
483 LOG(INFO) << "Error disassembling: " << error_msg;
484 std::remove(data_name.c_str());
485 std::remove((data_name + ".dis").c_str());
486 std::remove(as_name.c_str());
487 return false;
488 }
489
490 bool result = CompareFiles(data_name + ".dis", as_name + ".dis");
491
492 if (result) {
493 std::remove(data_name.c_str());
494 std::remove(as_name.c_str());
495 std::remove((data_name + ".dis").c_str());
496 std::remove((as_name + ".dis").c_str());
497 }
498
499 return result;
500 }
501
502 bool DisassembleBinary(std::string file, std::string* error_msg) {
503 std::vector<std::string> args;
504
Roland Levillain1a28fc42014-11-13 18:03:06 +0000505 // Encaspulate the whole command line in a single string passed to
506 // the shell, so that GetDisassembleCommand() may contain arguments
507 // in addition to the program name.
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700508 args.push_back(GetDisassembleCommand());
509 args.push_back(file);
510 args.push_back("| sed -n \'/<.data>/,$p\' | sed -e \'s/.*://\'");
511 args.push_back(">");
512 args.push_back(file+".dis");
513 std::string cmd = Join(args, ' ');
514
515 args.clear();
516 args.push_back("/bin/sh");
517 args.push_back("-c");
518 args.push_back(cmd);
519
520 return Exec(args, error_msg);
521 }
522
523 std::string WriteToFile(std::vector<uint8_t>& buffer, std::string test_name) {
524 std::string file_name = GetTmpnam() + std::string("---") + test_name;
525 const char* data = reinterpret_cast<char*>(buffer.data());
526 std::ofstream s_out(file_name + ".o");
527 s_out.write(data, buffer.size());
528 s_out.close();
529 return file_name + ".o";
530 }
531
532 bool CompareFiles(std::string f1, std::string f2) {
533 std::ifstream f1_in(f1);
534 std::ifstream f2_in(f2);
535
536 bool result = std::equal(std::istreambuf_iterator<char>(f1_in),
537 std::istreambuf_iterator<char>(),
538 std::istreambuf_iterator<char>(f2_in));
539
540 f1_in.close();
541 f2_in.close();
542
543 return result;
544 }
545
546 // Compile the given assembly code and extract the binary, if possible. Put result into res.
547 bool Compile(std::string assembly_code, NativeAssemblerResult* res, std::string test_name) {
548 res->ok = false;
549 res->code.reset(nullptr);
550
551 res->base_name = GetTmpnam() + std::string("---") + test_name;
552
553 // TODO: Lots of error checking.
554
555 std::ofstream s_out(res->base_name + ".S");
556 s_out << assembly_code;
557 s_out.close();
558
559 if (!Assemble((res->base_name + ".S").c_str(), (res->base_name + ".o").c_str(),
560 &res->error_msg)) {
561 res->error_msg = "Could not compile.";
562 return false;
563 }
564
565 std::string odump = Objdump(res->base_name + ".o");
566 if (odump.length() == 0) {
567 res->error_msg = "Objdump failed.";
568 return false;
569 }
570
571 std::istringstream iss(odump);
572 std::istream_iterator<std::string> start(iss);
573 std::istream_iterator<std::string> end;
574 std::vector<std::string> tokens(start, end);
575
576 if (tokens.size() < OBJDUMP_SECTION_LINE_MIN_TOKENS) {
577 res->error_msg = "Objdump output not recognized: too few tokens.";
578 return false;
579 }
580
581 if (tokens[1] != ".text") {
582 res->error_msg = "Objdump output not recognized: .text not second token.";
583 return false;
584 }
585
586 std::string lengthToken = "0x" + tokens[2];
587 std::istringstream(lengthToken) >> std::hex >> res->length;
588
589 std::string offsetToken = "0x" + tokens[5];
590 uintptr_t offset;
591 std::istringstream(offsetToken) >> std::hex >> offset;
592
593 std::ifstream obj(res->base_name + ".o");
594 obj.seekg(offset);
595 res->code.reset(new std::vector<uint8_t>(res->length));
596 obj.read(reinterpret_cast<char*>(&(*res->code)[0]), res->length);
597 obj.close();
598
599 res->ok = true;
600 return true;
601 }
602
603 // Remove temporary files.
604 void Clean(const NativeAssemblerResult* res) {
605 std::remove((res->base_name + ".S").c_str());
606 std::remove((res->base_name + ".o").c_str());
607 std::remove((res->base_name + ".o.dump").c_str());
608 }
609
610 // Check whether file exists. Is used for commands, so strips off any parameters: anything after
611 // the first space. We skip to the last slash for this, so it should work with directories with
612 // spaces.
613 static bool FileExists(std::string file) {
614 if (file.length() == 0) {
615 return false;
616 }
617
618 // Need to strip any options.
619 size_t last_slash = file.find_last_of('/');
620 if (last_slash == std::string::npos) {
621 // No slash, start looking at the start.
622 last_slash = 0;
623 }
624 size_t space_index = file.find(' ', last_slash);
625
626 if (space_index == std::string::npos) {
627 std::ifstream infile(file.c_str());
628 return infile.good();
629 } else {
630 std::string copy = file.substr(0, space_index - 1);
631
632 struct stat buf;
633 return stat(copy.c_str(), &buf) == 0;
634 }
635 }
636
637 static std::string GetGCCRootPath() {
638 return "prebuilts/gcc/linux-x86";
639 }
640
641 static std::string GetRootPath() {
642 // 1) Check ANDROID_BUILD_TOP
643 char* build_top = getenv("ANDROID_BUILD_TOP");
644 if (build_top != nullptr) {
645 return std::string(build_top) + "/";
646 }
647
648 // 2) Do cwd
649 char temp[1024];
650 return getcwd(temp, 1024) ? std::string(temp) + "/" : std::string("");
651 }
652
653 std::string FindTool(std::string tool_name) {
654 // Find the current tool. Wild-card pattern is "arch-string*tool-name".
655 std::string gcc_path = GetRootPath() + GetGCCRootPath();
656 std::vector<std::string> args;
657 args.push_back("find");
658 args.push_back(gcc_path);
659 args.push_back("-name");
660 args.push_back(GetArchitectureString() + "*" + tool_name);
661 args.push_back("|");
662 args.push_back("sort");
663 args.push_back("|");
664 args.push_back("tail");
665 args.push_back("-n");
666 args.push_back("1");
667 std::string tmp_file = GetTmpnam();
668 args.push_back(">");
669 args.push_back(tmp_file);
670 std::string sh_args = Join(args, ' ');
671
672 args.clear();
673 args.push_back("/bin/sh");
674 args.push_back("-c");
675 args.push_back(sh_args);
676
677 std::string error_msg;
678 if (!Exec(args, &error_msg)) {
679 EXPECT_TRUE(false) << error_msg;
680 return "";
681 }
682
683 std::ifstream in(tmp_file.c_str());
684 std::string line;
685 if (!std::getline(in, line)) {
686 in.close();
687 std::remove(tmp_file.c_str());
688 return "";
689 }
690 in.close();
691 std::remove(tmp_file.c_str());
692 return line;
693 }
694
695 // Use a consistent tmpnam, so store it.
696 std::string GetTmpnam() {
697 if (tmpnam_.length() == 0) {
Andreas Gampeb40c6a72014-05-02 14:25:12 -0700698 ScratchFile tmp;
699 tmpnam_ = tmp.GetFilename() + "asm";
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700700 }
701 return tmpnam_;
702 }
703
Ian Rogers700a4022014-05-19 16:49:03 -0700704 std::unique_ptr<Ass> assembler_;
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700705
706 std::string resolved_assembler_cmd_;
707 std::string resolved_objdump_cmd_;
708 std::string resolved_disassemble_cmd_;
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700709
Andreas Gampe7747c8d2014-08-06 14:53:03 -0700710 std::string android_data_;
711
Andreas Gampe5a4fa822014-03-31 16:50:12 -0700712 static constexpr size_t OBJDUMP_SECTION_LINE_MIN_TOKENS = 6;
713};
714
715} // namespace art
716
717#endif // ART_COMPILER_UTILS_ASSEMBLER_TEST_H_