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_OATDUMP_OATDUMP_TEST_H_ 18 #define ART_OATDUMP_OATDUMP_TEST_H_ 19 20 #include <sys/types.h> 21 #include <unistd.h> 22 23 #include <memory> 24 #include <sstream> 25 #include <string> 26 #include <type_traits> 27 #include <vector> 28 29 #include "arch/instruction_set.h" 30 #include "base/common_art_test.h" 31 #include "base/file_utils.h" 32 #include "base/os.h" 33 #include "common_runtime_test.h" 34 #include "gtest/gtest.h" 35 36 namespace art { 37 38 // Linking flavor. 39 enum class Flavor { 40 kDynamic, // oatdump(d), dex2oat(d) 41 kStatic, // oatdump(d)s, dex2oat(d)s 42 }; 43 44 class OatDumpTest : public CommonRuntimeTest, public testing::WithParamInterface<Flavor> { 45 protected: SetUp()46 virtual void SetUp() { 47 CommonRuntimeTest::SetUp(); 48 core_art_location_ = GetCoreArtLocation(); 49 core_oat_location_ = GetSystemImageFilename(GetCoreOatLocation().c_str(), kRuntimeISA); 50 tmp_dir_ = GetScratchDir(); 51 if (GetParam() == Flavor::kStatic) { 52 TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); 53 } 54 55 // Prevent boot image inference to ensure consistent test behavior. 56 unset_bootclasspath_ = std::make_unique<ScopedUnsetEnvironmentVariable>("BOOTCLASSPATH"); 57 } 58 TearDown()59 virtual void TearDown() { 60 unset_bootclasspath_.reset(); 61 ClearDirectory(tmp_dir_.c_str(), /*recursive*/ false); 62 ASSERT_EQ(rmdir(tmp_dir_.c_str()), 0); 63 CommonRuntimeTest::TearDown(); 64 } 65 GetScratchDir()66 std::string GetScratchDir() const { 67 // ANDROID_DATA needs to be set 68 CHECK_NE(static_cast<char*>(nullptr), getenv("ANDROID_DATA")); 69 std::string dir = getenv("ANDROID_DATA"); 70 dir += "/oatdump-tmp-dir-XXXXXX"; 71 if (mkdtemp(&dir[0]) == nullptr) { 72 PLOG(FATAL) << "mkdtemp(\"" << &dir[0] << "\") failed"; 73 } 74 return dir; 75 } 76 77 // Returns path to the oatdump/dex2oat/dexdump binary. GetExecutableFilePath(const char * name,bool is_debug,bool is_static,bool bitness)78 static std::string GetExecutableFilePath(const char* name, 79 bool is_debug, 80 bool is_static, 81 bool bitness) { 82 std::string path = GetArtBinDir() + '/' + name; 83 if (is_debug) { 84 path += 'd'; 85 } 86 if (is_static) { 87 path += 's'; 88 } 89 if (bitness) { 90 path += Is64BitInstructionSet(kRuntimeISA) ? "64" : "32"; 91 } 92 return path; 93 } 94 GetExecutableFilePath(Flavor flavor,const char * name,bool bitness)95 static std::string GetExecutableFilePath(Flavor flavor, const char* name, bool bitness) { 96 return GetExecutableFilePath(name, kIsDebugBuild, flavor == Flavor::kStatic, bitness); 97 } 98 99 enum Args { 100 kArgImage = 1 << 0, // --image=<boot-image> 101 kArgAppImage = 1 << 1, // --app-image=<app-image> 102 kArgOatBcp = 1 << 2, // --oat-file=<bcp-oat-file> 103 kArgDexBcp = 1 << 3, // --dex-file=<bcp-dex-file> 104 kArgOatApp = 1 << 4, // --oat-file=<app-oat-file> 105 kArgSymbolize = 1 << 5, // --symbolize=<bcp-oat-file> 106 kArgDexApp = 1 << 6, // --dex-file=<app-dex-file> 107 kArgMethodAndOffsetAsJson = 1 << 7, // --dump-method-and-offset-only 108 109 // Runtime args. 110 kArgBcp = 1 << 16, // --runtime-arg -Xbootclasspath:<bcp> 111 kArgBootImage = 1 << 17, // --boot-image=<boot-image> 112 kArgIsa = 1 << 18, // --instruction-set=<isa> 113 }; 114 115 enum Expects { 116 kExpectImage = 1 << 0, 117 kExpectOat = 1 << 1, 118 kExpectCode = 1 << 2, 119 kExpectBssMappingsForBcp = 1 << 3, 120 kExpectBssOffsetsForBcp = 1 << 4, 121 kExpectMethodAndOffsetAsJson = 1 << 5, 122 }; 123 GetAppBaseName()124 static std::string GetAppBaseName() { 125 // Use ProfileTestMultiDex as it contains references to boot image strings 126 // that shall use different code for PIC and non-PIC. 127 return "ProfileTestMultiDex"; 128 } 129 GetAppImageName()130 std::string GetAppImageName() const { return tmp_dir_ + "/" + GetAppBaseName() + ".art"; } 131 GetAppOdexName()132 std::string GetAppOdexName() const { return tmp_dir_ + "/" + GetAppBaseName() + ".odex"; } 133 134 ::testing::AssertionResult GenerateAppOdexFile(Flavor flavor, 135 const std::vector<std::string>& args = {}) const { 136 std::string dex2oat_path = 137 GetExecutableFilePath(flavor, "dex2oat", /* bitness= */ kIsTargetBuild); 138 std::vector<std::string> exec_argv = { 139 dex2oat_path, 140 "--runtime-arg", 141 "-Xms64m", 142 "--runtime-arg", 143 "-Xmx64m", 144 "--runtime-arg", 145 "-Xnorelocate", 146 "--runtime-arg", 147 GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames()), 148 "--runtime-arg", 149 GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations()), 150 "--boot-image=" + GetCoreArtLocation(), 151 "--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)), 152 "--dex-file=" + GetTestDexFileName(GetAppBaseName().c_str()), 153 "--oat-file=" + GetAppOdexName(), 154 "--compiler-filter=speed", 155 }; 156 exec_argv.insert(exec_argv.end(), args.begin(), args.end()); 157 158 auto post_fork_fn = []() { 159 setpgid(0, 0); // Change process groups, so we don't get reaped by ProcessManager. 160 // Ignore setpgid errors. 161 return setenv("ANDROID_LOG_TAGS", "*:e", 1) == 0; // We're only interested in errors and 162 // fatal logs. 163 }; 164 165 std::string error_msg; 166 ForkAndExecResult res = ForkAndExec(exec_argv, post_fork_fn, &error_msg); 167 if (res.stage != ForkAndExecResult::kFinished) { 168 return ::testing::AssertionFailure() << strerror(errno); 169 } 170 return res.StandardSuccess() ? ::testing::AssertionSuccess() 171 : (::testing::AssertionFailure() << error_msg); 172 } 173 174 // Run the test with custom arguments. 175 ::testing::AssertionResult Exec(Flavor flavor, 176 std::underlying_type_t<Args> args, 177 const std::vector<std::string>& extra_args, 178 std::underlying_type_t<Expects> expects, 179 bool expect_failure = false) const { 180 std::string file_path = GetExecutableFilePath(flavor, "oatdump", /* bitness= */ false); 181 182 if (!OS::FileExists(file_path.c_str())) { 183 return ::testing::AssertionFailure() << file_path << " should be a valid file path"; 184 } 185 186 std::vector<std::string> expected_prefixes; 187 if ((expects & kExpectImage) != 0) { 188 expected_prefixes.push_back("IMAGE LOCATION:"); 189 expected_prefixes.push_back("IMAGE BEGIN:"); 190 expected_prefixes.push_back("kDexCaches:"); 191 } 192 if ((expects & kExpectOat) != 0) { 193 expected_prefixes.push_back("LOCATION:"); 194 expected_prefixes.push_back("MAGIC:"); 195 expected_prefixes.push_back("DEX FILE COUNT:"); 196 } 197 if ((expects & kExpectCode) != 0) { 198 // Code and dex code do not show up if list only. 199 expected_prefixes.push_back("DEX CODE:"); 200 expected_prefixes.push_back("CODE:"); 201 expected_prefixes.push_back("StackMap"); 202 } 203 if ((expects & kExpectBssMappingsForBcp) != 0) { 204 expected_prefixes.push_back("Entries for BCP DexFile"); 205 } 206 if ((expects & kExpectBssOffsetsForBcp) != 0) { 207 expected_prefixes.push_back("Offsets for BCP DexFile"); 208 } 209 if ((expects & kExpectMethodAndOffsetAsJson) != 0) { 210 expected_prefixes.push_back( 211 "{\"method\":\"void java.lang.Object.<init>()\",\"offset\":\"0x"); // actual offset may 212 // differ between dex 213 // files 214 } 215 216 std::vector<std::string> exec_argv = {file_path}; 217 if ((args & kArgSymbolize) != 0) { 218 exec_argv.push_back("--symbolize=" + core_oat_location_); 219 exec_argv.push_back("--output=" + core_oat_location_ + ".symbolize"); 220 } 221 if ((args & kArgBcp) != 0) { 222 exec_argv.push_back("--runtime-arg"); 223 exec_argv.push_back(GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames())); 224 exec_argv.push_back("--runtime-arg"); 225 exec_argv.push_back( 226 GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations())); 227 } 228 if ((args & kArgIsa) != 0) { 229 exec_argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA))); 230 } 231 if ((args & kArgBootImage) != 0) { 232 exec_argv.push_back("--boot-image=" + GetCoreArtLocation()); 233 } 234 if ((args & kArgImage) != 0) { 235 exec_argv.push_back("--image=" + GetCoreArtLocation()); 236 } 237 if ((args & kArgAppImage) != 0) { 238 exec_argv.push_back("--app-image=" + GetAppImageName()); 239 } 240 if ((args & kArgOatBcp) != 0) { 241 exec_argv.push_back("--oat-file=" + core_oat_location_); 242 } 243 if ((args & kArgDexBcp) != 0) { 244 exec_argv.push_back("--dex-file=" + GetLibCoreDexFileNames()[0]); 245 } 246 if ((args & kArgOatApp) != 0) { 247 exec_argv.push_back("--oat-file=" + GetAppOdexName()); 248 } 249 if ((args & kArgDexApp) != 0) { 250 exec_argv.push_back("--dex-file=" + GetTestDexFileName(GetAppBaseName().c_str())); 251 } 252 if ((args & kArgMethodAndOffsetAsJson) != 0) { 253 exec_argv.push_back("--dump-method-and-offset-as-json"); 254 } 255 exec_argv.insert(exec_argv.end(), extra_args.begin(), extra_args.end()); 256 257 std::vector<bool> found(expected_prefixes.size(), false); 258 auto line_handle_fn = [&found, &expected_prefixes](const char* line, size_t line_len) { 259 if (line_len == 0) { 260 return; 261 } 262 // Check contents. 263 for (size_t i = 0; i < expected_prefixes.size(); ++i) { 264 const std::string& expected = expected_prefixes[i]; 265 if (!found[i] && 266 line_len >= expected.length() && 267 memcmp(line, expected.c_str(), expected.length()) == 0) { 268 found[i] = true; 269 } 270 } 271 }; 272 273 static constexpr size_t kLineMax = 256; 274 char line[kLineMax] = {}; 275 size_t line_len = 0; 276 size_t total = 0; 277 bool ignore_next_line = false; 278 std::vector<char> error_buf; // Buffer for debug output on error. Limited to 1M. 279 auto line_buf_fn = [&](char* buf, size_t len) { 280 total += len; 281 282 if (len == 0 && line_len > 0 && !ignore_next_line) { 283 // Everything done, handle leftovers. 284 line_handle_fn(line, line_len); 285 } 286 287 if (len > 0) { 288 size_t pos = error_buf.size(); 289 if (pos < MB) { 290 error_buf.insert(error_buf.end(), buf, buf + len); 291 } 292 } 293 294 while (len > 0) { 295 // Copy buf into the free tail of the line buffer, and move input buffer along. 296 size_t copy = std::min(kLineMax - line_len, len); 297 memcpy(&line[line_len], buf, copy); 298 buf += copy; 299 len -= copy; 300 301 // Skip spaces up to len, return count of removed spaces. Declare a lambda for reuse. 302 auto trim_space = [&line](size_t len) { 303 size_t spaces = 0; 304 for (; spaces < len && isspace(line[spaces]); ++spaces) {} 305 if (spaces > 0) { 306 memmove(&line[0], &line[spaces], len - spaces); 307 } 308 return spaces; 309 }; 310 // There can only be spaces if we freshly started a line. 311 if (line_len == 0) { 312 copy -= trim_space(copy); 313 } 314 315 // Scan for newline characters. 316 size_t index = line_len; 317 line_len += copy; 318 while (index < line_len) { 319 if (line[index] == '\n') { 320 // Handle line. 321 if (!ignore_next_line) { 322 line_handle_fn(line, index); 323 } 324 // Move the rest to the front, but trim leading spaces. 325 line_len -= index + 1; 326 memmove(&line[0], &line[index + 1], line_len); 327 line_len -= trim_space(line_len); 328 index = 0; 329 ignore_next_line = false; 330 } else { 331 index++; 332 } 333 } 334 335 // Handle a full line without newline characters. Ignore the "next" line, as it is the 336 // tail end of this. 337 if (line_len == kLineMax) { 338 if (!ignore_next_line) { 339 line_handle_fn(line, kLineMax); 340 } 341 line_len = 0; 342 ignore_next_line = true; 343 } 344 } 345 }; 346 347 auto post_fork_fn = []() { 348 setpgid(0, 0); // Change process groups, so we don't get reaped by ProcessManager. 349 // Ignore setpgid failures. 350 return setenv("ANDROID_LOG_TAGS", "*:e", 1) == 0; // We're only interested in errors and 351 // fatal logs. 352 }; 353 354 ForkAndExecResult res = ForkAndExec(exec_argv, post_fork_fn, line_buf_fn); 355 if (res.stage != ForkAndExecResult::kFinished) { 356 return ::testing::AssertionFailure() << strerror(errno); 357 } 358 error_buf.push_back(0); // Make data a C string. 359 360 if (!res.StandardSuccess()) { 361 if (expect_failure && WIFEXITED(res.status_code)) { 362 // Avoid crash as valid exit. 363 return ::testing::AssertionSuccess(); 364 } 365 std::ostringstream cmd; 366 std::copy(exec_argv.begin(), exec_argv.end(), std::ostream_iterator<std::string>(cmd, " ")); 367 LOG(ERROR) << "Output: " << error_buf.data(); // Output first as it might be extremely long. 368 LOG(ERROR) << "Failed command: " << cmd.str(); // Useful to reproduce the failure separately. 369 return ::testing::AssertionFailure() << "Did not terminate successfully: " << res.status_code; 370 } else if (expect_failure) { 371 return ::testing::AssertionFailure() << "Expected failure"; 372 } 373 374 if ((args & kArgSymbolize) != 0) { 375 EXPECT_EQ(total, 0u); 376 } else { 377 EXPECT_GT(total, 0u); 378 } 379 380 bool result = true; 381 std::ostringstream oss; 382 for (size_t i = 0; i < expected_prefixes.size(); ++i) { 383 if (!found[i]) { 384 oss << "Did not find prefix " << expected_prefixes[i] << std::endl; 385 result = false; 386 } 387 } 388 if (!result) { 389 oss << "Processed bytes " << total << ":" << std::endl; 390 } 391 392 return result ? ::testing::AssertionSuccess() 393 : (::testing::AssertionFailure() << oss.str() << error_buf.data()); 394 } 395 396 std::string tmp_dir_; 397 398 private: 399 std::string core_art_location_; 400 std::string core_oat_location_; 401 std::unique_ptr<ScopedUnsetEnvironmentVariable> unset_bootclasspath_; 402 }; 403 404 } // namespace art 405 406 #endif // ART_OATDUMP_OATDUMP_TEST_H_ 407