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 <sstream> 21 #include <string> 22 #include <vector> 23 24 #include "android-base/strings.h" 25 26 #include "arch/instruction_set.h" 27 #include "base/file_utils.h" 28 #include "base/os.h" 29 #include "base/unix_file/fd_file.h" 30 #include "base/utils.h" 31 #include "common_runtime_test.h" 32 #include "exec_utils.h" 33 #include "gc/heap.h" 34 #include "gc/space/image_space.h" 35 36 #include <sys/types.h> 37 #include <unistd.h> 38 39 namespace art { 40 41 class OatDumpTest : public CommonRuntimeTest { 42 protected: SetUp()43 virtual void SetUp() { 44 CommonRuntimeTest::SetUp(); 45 core_art_location_ = GetCoreArtLocation(); 46 core_oat_location_ = GetSystemImageFilename(GetCoreOatLocation().c_str(), kRuntimeISA); 47 tmp_dir_ = GetScratchDir(); 48 } 49 TearDown()50 virtual void TearDown() { 51 ClearDirectory(tmp_dir_.c_str(), /*recursive*/ false); 52 ASSERT_EQ(rmdir(tmp_dir_.c_str()), 0); 53 CommonRuntimeTest::TearDown(); 54 } 55 GetScratchDir()56 std::string GetScratchDir() { 57 // ANDROID_DATA needs to be set 58 CHECK_NE(static_cast<char*>(nullptr), getenv("ANDROID_DATA")); 59 std::string dir = getenv("ANDROID_DATA"); 60 dir += "/oatdump-tmp-dir-XXXXXX"; 61 if (mkdtemp(&dir[0]) == nullptr) { 62 PLOG(FATAL) << "mkdtemp(\"" << &dir[0] << "\") failed"; 63 } 64 return dir; 65 } 66 67 // Linking flavor. 68 enum Flavor { 69 kDynamic, // oatdump(d), dex2oat(d) 70 kStatic, // oatdump(d)s, dex2oat(d)s 71 }; 72 73 // Returns path to the oatdump/dex2oat/dexdump binary. GetExecutableFilePath(const char * name,bool is_debug,bool is_static)74 std::string GetExecutableFilePath(const char* name, bool is_debug, bool is_static) { 75 std::string root = GetTestAndroidRoot(); 76 root += "/bin/"; 77 root += name; 78 if (is_debug) { 79 root += "d"; 80 } 81 if (is_static) { 82 root += "s"; 83 } 84 return root; 85 } 86 GetExecutableFilePath(Flavor flavor,const char * name)87 std::string GetExecutableFilePath(Flavor flavor, const char* name) { 88 return GetExecutableFilePath(name, kIsDebugBuild, flavor == kStatic); 89 } 90 91 enum Mode { 92 kModeOat, 93 kModeCoreOat, 94 kModeOatWithBootImage, 95 kModeAppImage, 96 kModeArt, 97 kModeSymbolize, 98 }; 99 100 // Display style. 101 enum Display { 102 kListOnly, 103 kListAndCode 104 }; 105 GetAppBaseName()106 std::string GetAppBaseName() { 107 // Use ProfileTestMultiDex as it contains references to boot image strings 108 // that shall use different code for PIC and non-PIC. 109 return "ProfileTestMultiDex"; 110 } 111 GetAppImageName()112 std::string GetAppImageName() { 113 return tmp_dir_ + "/" + GetAppBaseName() + ".art"; 114 } 115 GetAppOdexName()116 std::string GetAppOdexName() { 117 return tmp_dir_ + "/" + GetAppBaseName() + ".odex"; 118 } 119 GenerateAppOdexFile(Flavor flavor,const std::vector<std::string> & args)120 ::testing::AssertionResult GenerateAppOdexFile(Flavor flavor, 121 const std::vector<std::string>& args) { 122 std::string dex2oat_path = GetExecutableFilePath(flavor, "dex2oat"); 123 std::vector<std::string> exec_argv = { 124 dex2oat_path, 125 "--runtime-arg", 126 "-Xms64m", 127 "--runtime-arg", 128 "-Xmx512m", 129 "--runtime-arg", 130 "-Xnorelocate", 131 "--runtime-arg", 132 GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames()), 133 "--runtime-arg", 134 GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations()), 135 "--boot-image=" + GetCoreArtLocation(), 136 "--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)), 137 "--dex-file=" + GetTestDexFileName(GetAppBaseName().c_str()), 138 "--oat-file=" + GetAppOdexName(), 139 "--compiler-filter=speed" 140 }; 141 exec_argv.insert(exec_argv.end(), args.begin(), args.end()); 142 143 auto post_fork_fn = []() { 144 setpgid(0, 0); // Change process groups, so we don't get reaped by ProcessManager. 145 // Ignore setpgid errors. 146 return setenv("ANDROID_LOG_TAGS", "*:e", 1) == 0; // We're only interested in errors and 147 // fatal logs. 148 }; 149 150 std::string error_msg; 151 ForkAndExecResult res = ForkAndExec(exec_argv, post_fork_fn, &error_msg); 152 if (res.stage != ForkAndExecResult::kFinished) { 153 return ::testing::AssertionFailure() << strerror(errno); 154 } 155 return res.StandardSuccess() ? ::testing::AssertionSuccess() 156 : (::testing::AssertionFailure() << error_msg); 157 } 158 159 // Run the test with custom arguments. Exec(Flavor flavor,Mode mode,const std::vector<std::string> & args,Display display)160 ::testing::AssertionResult Exec(Flavor flavor, 161 Mode mode, 162 const std::vector<std::string>& args, 163 Display display) { 164 std::string file_path = GetExecutableFilePath(flavor, "oatdump"); 165 166 if (!OS::FileExists(file_path.c_str())) { 167 return ::testing::AssertionFailure() << file_path << " should be a valid file path"; 168 } 169 170 // ScratchFile scratch; 171 std::vector<std::string> exec_argv = { file_path }; 172 std::vector<std::string> expected_prefixes; 173 if (mode == kModeSymbolize) { 174 exec_argv.push_back("--symbolize=" + core_oat_location_); 175 exec_argv.push_back("--output=" + core_oat_location_ + ".symbolize"); 176 } else { 177 expected_prefixes.push_back("LOCATION:"); 178 expected_prefixes.push_back("MAGIC:"); 179 expected_prefixes.push_back("DEX FILE COUNT:"); 180 if (display == kListAndCode) { 181 // Code and dex code do not show up if list only. 182 expected_prefixes.push_back("DEX CODE:"); 183 expected_prefixes.push_back("CODE:"); 184 expected_prefixes.push_back("InlineInfo"); 185 } 186 if (mode == kModeArt) { 187 exec_argv.push_back("--runtime-arg"); 188 exec_argv.push_back(GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames())); 189 exec_argv.push_back("--runtime-arg"); 190 exec_argv.push_back( 191 GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations())); 192 exec_argv.push_back("--image=" + core_art_location_); 193 exec_argv.push_back("--instruction-set=" + std::string( 194 GetInstructionSetString(kRuntimeISA))); 195 expected_prefixes.push_back("IMAGE LOCATION:"); 196 expected_prefixes.push_back("IMAGE BEGIN:"); 197 expected_prefixes.push_back("kDexCaches:"); 198 } else if (mode == kModeOatWithBootImage) { 199 exec_argv.push_back("--runtime-arg"); 200 exec_argv.push_back(GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames())); 201 exec_argv.push_back("--runtime-arg"); 202 exec_argv.push_back( 203 GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations())); 204 exec_argv.push_back("--boot-image=" + GetCoreArtLocation()); 205 exec_argv.push_back("--instruction-set=" + std::string( 206 GetInstructionSetString(kRuntimeISA))); 207 exec_argv.push_back("--oat-file=" + GetAppOdexName()); 208 } else if (mode == kModeAppImage) { 209 exec_argv.push_back("--runtime-arg"); 210 exec_argv.push_back(GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames())); 211 exec_argv.push_back("--runtime-arg"); 212 exec_argv.push_back( 213 GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations())); 214 exec_argv.push_back("--image=" + GetCoreArtLocation()); 215 exec_argv.push_back("--instruction-set=" + std::string( 216 GetInstructionSetString(kRuntimeISA))); 217 exec_argv.push_back("--app-oat=" + GetAppOdexName()); 218 exec_argv.push_back("--app-image=" + GetAppImageName()); 219 } else if (mode == kModeCoreOat) { 220 exec_argv.push_back("--oat-file=" + core_oat_location_); 221 } else { 222 CHECK_EQ(static_cast<size_t>(mode), static_cast<size_t>(kModeOat)); 223 exec_argv.push_back("--oat-file=" + GetAppOdexName()); 224 } 225 } 226 exec_argv.insert(exec_argv.end(), args.begin(), args.end()); 227 228 std::vector<bool> found(expected_prefixes.size(), false); 229 auto line_handle_fn = [&found, &expected_prefixes](const char* line, size_t line_len) { 230 if (line_len == 0) { 231 return; 232 } 233 // Check contents. 234 for (size_t i = 0; i < expected_prefixes.size(); ++i) { 235 const std::string& expected = expected_prefixes[i]; 236 if (!found[i] && 237 line_len >= expected.length() && 238 memcmp(line, expected.c_str(), expected.length()) == 0) { 239 found[i] = true; 240 } 241 } 242 }; 243 244 static constexpr size_t kLineMax = 256; 245 char line[kLineMax] = {}; 246 size_t line_len = 0; 247 size_t total = 0; 248 bool ignore_next_line = false; 249 std::vector<char> error_buf; // Buffer for debug output on error. Limited to 1M. 250 auto line_buf_fn = [&](char* buf, size_t len) { 251 total += len; 252 253 if (len == 0 && line_len > 0 && !ignore_next_line) { 254 // Everything done, handle leftovers. 255 line_handle_fn(line, line_len); 256 } 257 258 if (len > 0) { 259 size_t pos = error_buf.size(); 260 if (pos < MB) { 261 error_buf.insert(error_buf.end(), buf, buf + len); 262 } 263 } 264 265 while (len > 0) { 266 // Copy buf into the free tail of the line buffer, and move input buffer along. 267 size_t copy = std::min(kLineMax - line_len, len); 268 memcpy(&line[line_len], buf, copy); 269 buf += copy; 270 len -= copy; 271 272 // Skip spaces up to len, return count of removed spaces. Declare a lambda for reuse. 273 auto trim_space = [&line](size_t len) { 274 size_t spaces = 0; 275 for (; spaces < len && isspace(line[spaces]); ++spaces) {} 276 if (spaces > 0) { 277 memmove(&line[0], &line[spaces], len - spaces); 278 } 279 return spaces; 280 }; 281 // There can only be spaces if we freshly started a line. 282 if (line_len == 0) { 283 copy -= trim_space(copy); 284 } 285 286 // Scan for newline characters. 287 size_t index = line_len; 288 line_len += copy; 289 while (index < line_len) { 290 if (line[index] == '\n') { 291 // Handle line. 292 if (!ignore_next_line) { 293 line_handle_fn(line, index); 294 } 295 // Move the rest to the front, but trim leading spaces. 296 line_len -= index + 1; 297 memmove(&line[0], &line[index + 1], line_len); 298 line_len -= trim_space(line_len); 299 index = 0; 300 ignore_next_line = false; 301 } else { 302 index++; 303 } 304 } 305 306 // Handle a full line without newline characters. Ignore the "next" line, as it is the 307 // tail end of this. 308 if (line_len == kLineMax) { 309 if (!ignore_next_line) { 310 line_handle_fn(line, kLineMax); 311 } 312 line_len = 0; 313 ignore_next_line = true; 314 } 315 } 316 }; 317 318 auto post_fork_fn = []() { 319 setpgid(0, 0); // Change process groups, so we don't get reaped by ProcessManager. 320 return true; // Ignore setpgid failures. 321 }; 322 323 ForkAndExecResult res = ForkAndExec(exec_argv, post_fork_fn, line_buf_fn); 324 if (res.stage != ForkAndExecResult::kFinished) { 325 return ::testing::AssertionFailure() << strerror(errno); 326 } 327 if (!res.StandardSuccess()) { 328 return ::testing::AssertionFailure() << "Did not terminate successfully: " << res.status_code; 329 } 330 331 if (mode == kModeSymbolize) { 332 EXPECT_EQ(total, 0u); 333 } else { 334 EXPECT_GT(total, 0u); 335 } 336 337 bool result = true; 338 std::ostringstream oss; 339 for (size_t i = 0; i < expected_prefixes.size(); ++i) { 340 if (!found[i]) { 341 oss << "Did not find prefix " << expected_prefixes[i] << std::endl; 342 result = false; 343 } 344 } 345 if (!result) { 346 oss << "Processed bytes " << total << ":" << std::endl; 347 error_buf.push_back(0); // Make data a C string. 348 } 349 350 return result ? ::testing::AssertionSuccess() 351 : (::testing::AssertionFailure() << oss.str() << error_buf.data()); 352 } 353 354 std::string tmp_dir_; 355 356 private: 357 std::string core_art_location_; 358 std::string core_oat_location_; 359 }; 360 361 } // namespace art 362 363 #endif // ART_OATDUMP_OATDUMP_TEST_H_ 364