xref: /aosp_15_r20/external/cronet/net/tools/disk_cache_memory_test/disk_cache_memory_test.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <cstdlib>
6 #include <fstream>
7 #include <iostream>
8 #include <memory>
9 #include <string>
10 #include <vector>
11 
12 #include "base/at_exit.h"
13 #include "base/command_line.h"
14 #include "base/files/file_path.h"
15 #include "base/functional/bind.h"
16 #include "base/functional/callback.h"
17 #include "base/logging.h"
18 #include "base/memory/ptr_util.h"
19 #include "base/message_loop/message_pump_type.h"
20 #include "base/run_loop.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_piece.h"
23 #include "base/strings/string_split.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/task/single_thread_task_executor.h"
27 #include "base/task/thread_pool/thread_pool_instance.h"
28 #include "net/base/cache_type.h"
29 #include "net/base/net_errors.h"
30 #include "net/disk_cache/disk_cache.h"
31 #include "net/disk_cache/simple/simple_backend_impl.h"
32 #include "net/disk_cache/simple/simple_index.h"
33 
34 namespace disk_cache {
35 namespace {
36 
37 const char kBlockFileBackendType[] = "block_file";
38 const char kSimpleBackendType[] = "simple";
39 
40 const char kDiskCacheType[] = "disk_cache";
41 const char kAppCacheType[] = "app_cache";
42 
43 const char kPrivateDirty[] = "Private_Dirty:";
44 const char kReadWrite[] = "rw-";
45 const char kHeap[] = "[heap]";
46 const char kKb[] = "kB";
47 
48 struct CacheSpec {
49  public:
Parsedisk_cache::__anonce43f2e40111::CacheSpec50   static std::unique_ptr<CacheSpec> Parse(const std::string& spec_string) {
51     std::vector<std::string> tokens = base::SplitString(
52         spec_string, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
53     if (tokens.size() != 3)
54       return nullptr;
55     if (tokens[0] != kBlockFileBackendType && tokens[0] != kSimpleBackendType)
56       return nullptr;
57     if (tokens[1] != kDiskCacheType && tokens[1] != kAppCacheType)
58       return nullptr;
59     return base::WrapUnique(new CacheSpec(
60         tokens[0] == kBlockFileBackendType ? net::CACHE_BACKEND_BLOCKFILE
61                                            : net::CACHE_BACKEND_SIMPLE,
62         tokens[1] == kDiskCacheType ? net::DISK_CACHE : net::APP_CACHE,
63         base::FilePath(tokens[2])));
64   }
65 
66   const net::BackendType backend_type;
67   const net::CacheType cache_type;
68   const base::FilePath path;
69 
70  private:
CacheSpecdisk_cache::__anonce43f2e40111::CacheSpec71   CacheSpec(net::BackendType backend_type,
72             net::CacheType cache_type,
73             const base::FilePath& path)
74       : backend_type(backend_type),
75         cache_type(cache_type),
76         path(path) {
77   }
78 };
79 
SetSuccessCodeOnCompletion(base::RunLoop * run_loop,bool * succeeded,int net_error)80 void SetSuccessCodeOnCompletion(base::RunLoop* run_loop,
81                                 bool* succeeded,
82                                 int net_error) {
83   if (net_error == net::OK) {
84     *succeeded = true;
85   } else {
86     *succeeded = false;
87   }
88   run_loop->Quit();
89 }
90 
CreateAndInitBackend(const CacheSpec & spec)91 std::unique_ptr<Backend> CreateAndInitBackend(const CacheSpec& spec) {
92   base::RunLoop run_loop;
93   BackendResult result;
94   result = CreateCacheBackend(
95       spec.cache_type, spec.backend_type, /*file_operations=*/nullptr,
96       spec.path, 0, disk_cache::ResetHandling::kNeverReset, /*net_log=*/nullptr,
97       base::BindOnce(
98           [](BackendResult* out, base::RunLoop* run_loop,
99              BackendResult async_result) {
100             *out = std::move(async_result);
101             run_loop->Quit();
102           },
103           &result, &run_loop));
104   if (result.net_error == net::ERR_IO_PENDING)
105     run_loop.Run();
106   if (result.net_error != net::OK) {
107     LOG(ERROR) << "Could not initialize backend in "
108                << spec.path.LossyDisplayName();
109     return nullptr;
110   }
111   // For the simple cache, the index may not be initialized yet.
112   bool succeeded = false;
113   if (spec.backend_type == net::CACHE_BACKEND_SIMPLE) {
114     base::RunLoop index_run_loop;
115     net::CompletionOnceCallback index_callback = base::BindOnce(
116         &SetSuccessCodeOnCompletion, &index_run_loop, &succeeded);
117     SimpleBackendImpl* simple_backend =
118         static_cast<SimpleBackendImpl*>(result.backend.get());
119     simple_backend->index()->ExecuteWhenReady(std::move(index_callback));
120     index_run_loop.Run();
121     if (!succeeded) {
122       LOG(ERROR) << "Could not initialize Simple Cache in "
123                  << spec.path.LossyDisplayName();
124       return nullptr;
125     }
126   }
127   DCHECK(result.backend);
128   return std::move(result.backend);
129 }
130 
131 // Parses range lines from /proc/<PID>/smaps, e.g. (anonymous read write):
132 // 7f819d88b000-7f819d890000 rw-p 00000000 00:00 0
ParseRangeLine(const std::string & line,std::vector<std::string> * tokens,bool * is_anonymous_read_write)133 bool ParseRangeLine(const std::string& line,
134                     std::vector<std::string>* tokens,
135                     bool* is_anonymous_read_write) {
136   *tokens = base::SplitString(line, base::kWhitespaceASCII,
137                               base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
138   if (tokens->size() == 5) {
139     const std::string& mode = (*tokens)[1];
140     *is_anonymous_read_write = !mode.compare(0, 3, kReadWrite);
141     return true;
142   }
143   // On Android, most of the memory is allocated in the heap, instead of being
144   // mapped.
145   if (tokens->size() == 6) {
146     const std::string& type = (*tokens)[5];
147     *is_anonymous_read_write = (type == kHeap);
148     return true;
149   }
150   return false;
151 }
152 
153 // Parses range property lines from /proc/<PID>/smaps, e.g.:
154 // Private_Dirty:        16 kB
155 //
156 // Returns |false| iff it recognizes a new range line. Outputs non-zero |size|
157 // only if parsing succeeded.
ParseRangeProperty(const std::string & line,std::vector<std::string> * tokens,uint64_t * size,bool * is_private_dirty)158 bool ParseRangeProperty(const std::string& line,
159                         std::vector<std::string>* tokens,
160                         uint64_t* size,
161                         bool* is_private_dirty) {
162   *tokens = base::SplitString(line, base::kWhitespaceASCII,
163                               base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
164 
165   // If the line is long, attempt to parse new range outside of this scope.
166   if (tokens->size() > 3)
167     return false;
168 
169   // Skip the line on other parsing error occasions.
170   if (tokens->size() < 3)
171     return true;
172   const std::string& type = (*tokens)[0];
173   if (type != kPrivateDirty)
174     return true;
175   const std::string& unit = (*tokens)[2];
176   if (unit != kKb) {
177     LOG(WARNING) << "Discarding value not in kB: " << line;
178     return true;
179   }
180   const std::string& size_str = (*tokens)[1];
181   uint64_t map_size = 0;
182   if (!base::StringToUint64(size_str, &map_size))
183     return true;
184   *is_private_dirty = true;
185   *size = map_size;
186   return true;
187 }
188 
GetMemoryConsumption()189 uint64_t GetMemoryConsumption() {
190   std::ifstream maps_file(
191       base::StringPrintf("/proc/%d/smaps", getpid()).c_str());
192   if (!maps_file.good()) {
193     LOG(ERROR) << "Could not open smaps file.";
194     return false;
195   }
196   std::string line;
197   std::vector<std::string> tokens;
198   uint64_t total_size = 0;
199   if (!std::getline(maps_file, line) || line.empty())
200     return total_size;
201   while (true) {
202     bool is_anonymous_read_write = false;
203     if (!ParseRangeLine(line, &tokens, &is_anonymous_read_write)) {
204       LOG(WARNING) << "Parsing smaps - did not expect line: " << line;
205     }
206     if (!std::getline(maps_file, line) || line.empty())
207       return total_size;
208     bool is_private_dirty = false;
209     uint64_t size = 0;
210     while (ParseRangeProperty(line, &tokens, &size, &is_private_dirty)) {
211       if (is_anonymous_read_write && is_private_dirty) {
212         total_size += size;
213         is_private_dirty = false;
214       }
215       if (!std::getline(maps_file, line) || line.empty())
216         return total_size;
217     }
218   }
219 }
220 
CacheMemTest(const std::vector<std::unique_ptr<CacheSpec>> & specs)221 bool CacheMemTest(const std::vector<std::unique_ptr<CacheSpec>>& specs) {
222   std::vector<std::unique_ptr<Backend>> backends;
223   for (const auto& it : specs) {
224     std::unique_ptr<Backend> backend = CreateAndInitBackend(*it);
225     if (!backend)
226       return false;
227     std::cout << "Number of entries in " << it->path.LossyDisplayName() << " : "
228               << backend->GetEntryCount() << std::endl;
229     backends.push_back(std::move(backend));
230   }
231   const uint64_t memory_consumption = GetMemoryConsumption();
232   std::cout << "Private dirty memory: " << memory_consumption << " kB"
233             << std::endl;
234   return true;
235 }
236 
PrintUsage(std::ostream * stream)237 void PrintUsage(std::ostream* stream) {
238   *stream << "Usage: disk_cache_mem_test "
239           << "--spec-1=<spec> "
240           << "[--spec-2=<spec>]"
241           << std::endl
242           << "  with <cache_spec>=<backend_type>:<cache_type>:<cache_path>"
243           << std::endl
244           << "       <backend_type>='block_file'|'simple'" << std::endl
245           << "       <cache_type>='disk_cache'|'app_cache'" << std::endl
246           << "       <cache_path>=file system path" << std::endl;
247 }
248 
ParseAndStoreSpec(const std::string & spec_str,std::vector<std::unique_ptr<CacheSpec>> * specs)249 bool ParseAndStoreSpec(const std::string& spec_str,
250                        std::vector<std::unique_ptr<CacheSpec>>* specs) {
251   std::unique_ptr<CacheSpec> spec = CacheSpec::Parse(spec_str);
252   if (!spec) {
253     PrintUsage(&std::cerr);
254     return false;
255   }
256   specs->push_back(std::move(spec));
257   return true;
258 }
259 
Main(int argc,char ** argv)260 bool Main(int argc, char** argv) {
261   base::AtExitManager at_exit_manager;
262   base::SingleThreadTaskExecutor executor(base::MessagePumpType::IO);
263   base::ThreadPoolInstance::CreateAndStartWithDefaultParams(
264       "disk_cache_memory_test");
265   base::CommandLine::Init(argc, argv);
266   const base::CommandLine& command_line =
267       *base::CommandLine::ForCurrentProcess();
268   if (command_line.HasSwitch("help")) {
269     PrintUsage(&std::cout);
270     return true;
271   }
272   if ((command_line.GetSwitches().size() != 1 &&
273        command_line.GetSwitches().size() != 2) ||
274       !command_line.HasSwitch("spec-1") ||
275       (command_line.GetSwitches().size() == 2 &&
276        !command_line.HasSwitch("spec-2"))) {
277     PrintUsage(&std::cerr);
278     return false;
279   }
280   std::vector<std::unique_ptr<CacheSpec>> specs;
281   const std::string spec_str_1 = command_line.GetSwitchValueASCII("spec-1");
282   if (!ParseAndStoreSpec(spec_str_1, &specs))
283     return false;
284   if (command_line.HasSwitch("spec-2")) {
285     const std::string spec_str_2 = command_line.GetSwitchValueASCII("spec-2");
286     if (!ParseAndStoreSpec(spec_str_2, &specs))
287       return false;
288   }
289   return CacheMemTest(specs);
290 }
291 
292 }  // namespace
293 }  // namespace disk_cache
294 
main(int argc,char ** argv)295 int main(int argc, char** argv) {
296   return !disk_cache::Main(argc, argv);
297 }
298