1/*
2 * Copyright (C) 2015 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_DWARF_DWARF_TEST_H_
18#define ART_COMPILER_DWARF_DWARF_TEST_H_
19
20#include <cstring>
21#include <dirent.h>
22#include <memory>
23#include <set>
24#include <stdio.h>
25#include <string>
26#include <sys/types.h>
27
28#include "base/unix_file/fd_file.h"
29#include "common_runtime_test.h"
30#include "elf_builder.h"
31#include "gtest/gtest.h"
32#include "os.h"
33
34namespace art {
35namespace dwarf {
36
37#define DW_CHECK(substring) Check(substring, false, __FILE__, __LINE__)
38#define DW_CHECK_NEXT(substring) Check(substring, true, __FILE__, __LINE__)
39
40class DwarfTest : public CommonRuntimeTest {
41 public:
42  static constexpr bool kPrintObjdumpOutput = false;  // debugging.
43
44  struct ExpectedLine {
45    std::string substring;
46    bool next;
47    const char* at_file;
48    int at_line;
49  };
50
51  // Check that the objdump output contains given output.
52  // If next is true, it must be the next line.  Otherwise lines are skipped.
53  void Check(const char* substr, bool next, const char* at_file, int at_line) {
54    expected_lines_.push_back(ExpectedLine {substr, next, at_file, at_line});
55  }
56
57  // Pretty-print the generated DWARF data using objdump.
58  template<typename ElfTypes>
59  std::vector<std::string> Objdump(const char* args) {
60    // Write simple elf file with just the DWARF sections.
61    InstructionSet isa = (sizeof(typename ElfTypes::Addr) == 8) ? kX86_64 : kX86;
62    class NoCode : public CodeOutput {
63      bool Write(OutputStream*) OVERRIDE { return true; }  // NOLINT
64    } no_code;
65    ElfBuilder<ElfTypes> builder(isa, 0, &no_code, 0, &no_code, 0);
66    typedef typename ElfBuilder<ElfTypes>::RawSection RawSection;
67    RawSection debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
68    RawSection debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
69    RawSection debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
70    RawSection debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
71    RawSection debug_frame(".debug_frame", SHT_PROGBITS, 0, nullptr, 0, 8, 0);
72    if (!debug_info_data_.empty()) {
73      debug_info.SetBuffer(debug_info_data_);
74      builder.RegisterSection(&debug_info);
75    }
76    if (!debug_abbrev_data_.empty()) {
77      debug_abbrev.SetBuffer(debug_abbrev_data_);
78      builder.RegisterSection(&debug_abbrev);
79    }
80    if (!debug_str_data_.empty()) {
81      debug_str.SetBuffer(debug_str_data_);
82      builder.RegisterSection(&debug_str);
83    }
84    if (!debug_line_data_.empty()) {
85      debug_line.SetBuffer(debug_line_data_);
86      builder.RegisterSection(&debug_line);
87    }
88    if (!debug_frame_data_.empty()) {
89      debug_frame.SetBuffer(debug_frame_data_);
90      builder.RegisterSection(&debug_frame);
91    }
92    ScratchFile file;
93    builder.Write(file.GetFile());
94
95    // Read the elf file back using objdump.
96    std::vector<std::string> lines;
97    std::string cmd = GetAndroidHostToolsDir();
98    cmd = cmd + "objdump " + args + " " + file.GetFilename() + " 2>&1";
99    FILE* output = popen(cmd.data(), "r");
100    char buffer[1024];
101    const char* line;
102    while ((line = fgets(buffer, sizeof(buffer), output)) != nullptr) {
103      if (kPrintObjdumpOutput) {
104        printf("%s", line);
105      }
106      if (line[0] != '\0' && line[0] != '\n') {
107        EXPECT_TRUE(strstr(line, "objdump: Error:") == nullptr) << line;
108        EXPECT_TRUE(strstr(line, "objdump: Warning:") == nullptr) << line;
109        std::string str(line);
110        if (str.back() == '\n') {
111          str.pop_back();
112        }
113        lines.push_back(str);
114      }
115    }
116    pclose(output);
117    return lines;
118  }
119
120  std::vector<std::string> Objdump(bool is64bit, const char* args) {
121    if (is64bit) {
122      return Objdump<ElfTypes64>(args);
123    } else {
124      return Objdump<ElfTypes32>(args);
125    }
126  }
127
128  // Compare objdump output to the recorded checks.
129  void CheckObjdumpOutput(bool is64bit, const char* args) {
130    std::vector<std::string> actual_lines = Objdump(is64bit, args);
131    auto actual_line = actual_lines.begin();
132    for (const ExpectedLine& expected_line : expected_lines_) {
133      const std::string& substring = expected_line.substring;
134      if (actual_line == actual_lines.end()) {
135        ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) <<
136            "Expected '" << substring << "'.\n" <<
137            "Seen end of output.";
138      } else if (expected_line.next) {
139        if (actual_line->find(substring) == std::string::npos) {
140          ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) <<
141            "Expected '" << substring << "'.\n" <<
142            "Seen '" << actual_line->data() << "'.";
143        } else {
144          // printf("Found '%s' in '%s'.\n", substring.data(), actual_line->data());
145        }
146        actual_line++;
147      } else {
148        bool found = false;
149        for (auto it = actual_line; it < actual_lines.end(); it++) {
150          if (it->find(substring) != std::string::npos) {
151            actual_line = it;
152            found = true;
153            break;
154          }
155        }
156        if (!found) {
157          ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) <<
158            "Expected '" << substring << "'.\n" <<
159            "Not found anywhere in the rest of the output.";
160        } else {
161          // printf("Found '%s' in '%s'.\n", substring.data(), actual_line->data());
162          actual_line++;
163        }
164      }
165    }
166  }
167
168  // Buffers which are going to assembled into ELF file and passed to objdump.
169  std::vector<uint8_t> debug_frame_data_;
170  std::vector<uint8_t> debug_info_data_;
171  std::vector<uint8_t> debug_abbrev_data_;
172  std::vector<uint8_t> debug_str_data_;
173  std::vector<uint8_t> debug_line_data_;
174
175  // The expected output of objdump.
176  std::vector<ExpectedLine> expected_lines_;
177};
178
179}  // namespace dwarf
180}  // namespace art
181
182#endif  // ART_COMPILER_DWARF_DWARF_TEST_H_
183