test.h 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. // Copyright 2016 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #ifndef BLOATY_TESTS_TEST_H_
  15. #define BLOATY_TESTS_TEST_H_
  16. #include <fstream>
  17. #include <memory>
  18. #include <string>
  19. #include <unordered_set>
  20. #include <tuple>
  21. #include <vector>
  22. #include "absl/strings/numbers.h"
  23. #include "absl/strings/str_split.h"
  24. #include "gmock/gmock.h"
  25. #include "google/protobuf/text_format.h"
  26. #include "gtest/gtest.h"
  27. #include "strarr.h"
  28. #include "bloaty.h"
  29. #include "bloaty.pb.h"
  30. #if defined(_MSC_VER)
  31. #define PATH_MAX 4096
  32. #endif
  33. inline bool GetFileSize(const std::string& filename, uint64_t* size) {
  34. FILE* file = fopen(filename.c_str(), "rb");
  35. if (!file) {
  36. std::cerr << "Couldn't get file size for: " << filename << "\n";
  37. return false;
  38. }
  39. fseek(file, 0L, SEEK_END);
  40. *size = ftell(file);
  41. fclose(file);
  42. return true;
  43. }
  44. inline std::string GetTestDirectory() {
  45. char pathbuf[PATH_MAX];
  46. if (!getcwd(pathbuf, sizeof(pathbuf))) {
  47. return "";
  48. }
  49. std::string path(pathbuf);
  50. size_t pos = path.rfind('/');
  51. return path.substr(pos + 1);
  52. }
  53. inline std::string DebugString(const google::protobuf::Message& message) {
  54. std::string ret;
  55. google::protobuf::TextFormat::PrintToString(message, &ret);
  56. return ret;
  57. }
  58. #define NONE_STRING "[None]"
  59. // Testing Bloaty requires a delicate balance. Bloaty's output is by its
  60. // nature very compiler and platform dependent. So we want to verify correct
  61. // operation without overspecifying how the platform should behave.
  62. class BloatyTest : public ::testing::Test {
  63. protected:
  64. void CheckConsistencyForRow(const bloaty::RollupRow& row, bool is_toplevel,
  65. bool diff_mode, int* count) {
  66. // If any children exist, they should sum up to this row's values.
  67. // Also none of the children should have the same name.
  68. std::unordered_set<std::string> names;
  69. if (row.sorted_children.size() > 0) {
  70. uint64_t vmtotal = 0;
  71. uint64_t filetotal = 0;
  72. for (const auto& child : row.sorted_children) {
  73. vmtotal += child.vmsize;
  74. filetotal += child.filesize;
  75. CheckConsistencyForRow(child, false, diff_mode, count);
  76. ASSERT_TRUE(names.insert(child.name).second);
  77. ASSERT_FALSE(child.vmsize == 0 && child.filesize == 0);
  78. }
  79. if (!diff_mode) {
  80. ASSERT_EQ(vmtotal, row.vmsize);
  81. ASSERT_EQ(filetotal, row.filesize);
  82. }
  83. } else {
  84. // Count leaf rows.
  85. *count += 1;
  86. }
  87. if (!is_toplevel && row.sorted_children.size() == 1) {
  88. ASSERT_NE(NONE_STRING, row.sorted_children[0].name);
  89. }
  90. }
  91. void CheckCSVConsistency(int row_count) {
  92. std::ostringstream stream;
  93. bloaty::OutputOptions options;
  94. options.output_format = bloaty::OutputFormat::kCSV;
  95. output_->Print(options, &stream);
  96. std::string csv_output = stream.str();
  97. std::vector<std::string> rows = absl::StrSplit(csv_output, '\n');
  98. // Output ends with a final '\n', trim this.
  99. ASSERT_EQ("", rows[rows.size() - 1]);
  100. rows.pop_back();
  101. ASSERT_GT(rows.size(), 0); // There should be a header row.
  102. ASSERT_EQ(rows.size() - 1, row_count);
  103. bool first = true;
  104. for (const auto& row : rows) {
  105. std::vector<std::string> cols = absl::StrSplit(row, ',');
  106. if (first) {
  107. // header row should be: header1,header2,...,vmsize,filesize
  108. std::vector<std::string> expected_headers(output_->source_names());
  109. expected_headers.push_back("vmsize");
  110. expected_headers.push_back("filesize");
  111. ASSERT_EQ(cols, expected_headers);
  112. first = false;
  113. } else {
  114. // Final two columns should parse as integer.
  115. int out;
  116. ASSERT_EQ(output_->source_names().size() + 2, cols.size());
  117. ASSERT_TRUE(absl::SimpleAtoi(cols[cols.size() - 1], &out));
  118. ASSERT_TRUE(absl::SimpleAtoi(cols[cols.size() - 2], &out));
  119. }
  120. }
  121. }
  122. void CheckConsistency(const bloaty::Options& options) {
  123. ASSERT_EQ(options.base_filename_size() > 0, output_->diff_mode());
  124. if (!output_->diff_mode()) {
  125. size_t total_input_size = 0;
  126. for (const auto& filename : options.filename()) {
  127. uint64_t size;
  128. ASSERT_TRUE(GetFileSize(filename, &size));
  129. total_input_size += size;
  130. }
  131. ASSERT_EQ(top_row_->filesize, total_input_size);
  132. }
  133. int rows = 0;
  134. CheckConsistencyForRow(*top_row_, true, output_->diff_mode(), &rows);
  135. CheckCSVConsistency(rows);
  136. ASSERT_EQ("TOTAL", top_row_->name);
  137. }
  138. std::string JoinStrings(const std::vector<std::string>& strings) {
  139. std::string ret = strings[0];
  140. for (size_t i = 1; i < strings.size(); i++) {
  141. ret += " " + strings[i];
  142. }
  143. return ret;
  144. }
  145. bool TryRunBloatyWithOptions(const bloaty::Options& options,
  146. const bloaty::OutputOptions& output_options) {
  147. output_.reset(new bloaty::RollupOutput);
  148. top_row_ = &output_->toplevel_row();
  149. std::string error;
  150. bloaty::MmapInputFileFactory factory;
  151. if (bloaty::BloatyMain(options, factory, output_.get(), &error)) {
  152. CheckConsistency(options);
  153. output_->Print(output_options, &std::cerr);
  154. return true;
  155. } else {
  156. std::cerr << "Bloaty returned error:" << error << "\n";
  157. return false;
  158. }
  159. }
  160. bool TryRunBloaty(const std::vector<std::string>& strings) {
  161. bloaty::Options options;
  162. bloaty::OutputOptions output_options;
  163. std::string error;
  164. StrArr str_arr(strings);
  165. int argc = strings.size();
  166. char** argv = str_arr.get();
  167. bool ok = bloaty::ParseOptions(false, &argc, &argv, &options,
  168. &output_options, &error);
  169. if (!ok) {
  170. std::cerr << "Error parsing options: " << error;
  171. return false;
  172. }
  173. return TryRunBloatyWithOptions(options, output_options);
  174. }
  175. void RunBloaty(const std::vector<std::string>& strings) {
  176. std::cerr << "Running bloaty: " << JoinStrings(strings) << "\n";
  177. ASSERT_TRUE(TryRunBloaty(strings));
  178. }
  179. void RunBloatyWithOptions(const bloaty::Options& options,
  180. const bloaty::OutputOptions& output_options) {
  181. std::cerr << "Running bloaty, options: " << DebugString(options) << "\n";
  182. ASSERT_TRUE(TryRunBloatyWithOptions(options, output_options));
  183. }
  184. void AssertBloatyFails(const std::vector<std::string>& strings,
  185. const std::string& /*msg_regex*/) {
  186. // TODO(haberman): verify msg_regex by making all errors logged to a
  187. // standard place.
  188. ASSERT_FALSE(TryRunBloaty(strings));
  189. }
  190. // Special constants for asserting of children.
  191. static constexpr int kUnknown = -1;
  192. static constexpr int kSameAsVM = -2; // Only for file size.
  193. void AssertChildren(
  194. const bloaty::RollupRow& row,
  195. const std::vector<std::tuple<std::string, int, int>>& children) {
  196. size_t i = 0;
  197. for (const auto& child : row.sorted_children) {
  198. std::string expected_name;
  199. int expected_vm, expected_file;
  200. std::tie(expected_name, expected_vm, expected_file) = children[i];
  201. // Excluding leading '_' is kind of a hack to exclude symbols
  202. // automatically inserted by the compiler, like __x86.get_pc_thunk.bx
  203. // for 32-bit x86 builds or _IO_stdin_used in binaries.
  204. //
  205. // Excluding leading '[' is for things like this:
  206. //
  207. // [None]
  208. // [ELF Headers]
  209. // [AR Headers]
  210. // etc.
  211. if (child.name[0] == '[' || child.name[0] == '_') {
  212. continue;
  213. }
  214. EXPECT_EQ(expected_name, child.name);
  215. // <0 indicates that we don't know what the exact size should be (for
  216. // example for functions).
  217. if (expected_vm == kUnknown) {
  218. // Always pass.
  219. } else if (expected_vm > 0) {
  220. EXPECT_GE(child.vmsize, expected_vm);
  221. // Allow some overhead.
  222. EXPECT_LE(child.vmsize, (expected_vm * 1.1) + 100);
  223. } else {
  224. ASSERT_TRUE(false);
  225. }
  226. if (expected_file == kSameAsVM) {
  227. expected_file = child.vmsize;
  228. }
  229. if (expected_file != kUnknown) {
  230. EXPECT_GE(child.filesize, expected_file);
  231. // Allow some overhead.
  232. EXPECT_LE(child.filesize, (expected_file * 1.2) + 180);
  233. }
  234. if (++i == children.size()) {
  235. // We allow the actual data to have excess elements.
  236. break;
  237. }
  238. }
  239. // All expected elements must be present.
  240. ASSERT_EQ(i, children.size());
  241. }
  242. const bloaty::RollupRow* FindRow(const std::string& name) {
  243. for (const auto& child : top_row_->sorted_children) {
  244. if (child.name == name) {
  245. return &child;
  246. }
  247. }
  248. EXPECT_TRUE(false) << name;
  249. return nullptr;
  250. }
  251. std::unique_ptr<bloaty::RollupOutput> output_;
  252. const bloaty::RollupRow* top_row_;
  253. };
  254. constexpr int BloatyTest::kUnknown;
  255. constexpr int BloatyTest::kSameAsVM;
  256. #endif // BLOATY_TESTS_TEST_H_