blob: 5df8fb5c0e29362e2bf5ee6e8127e377549dcd8a [file] [log] [blame]
yro947fbce2017-11-15 22:50:23 -08001/*
2 * Copyright (C) 2017 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
Tej Singh484524a2018-02-01 15:10:05 -080017#define DEBUG false // STOPSHIP if true
yro947fbce2017-11-15 22:50:23 -080018#include "Log.h"
19
yro947fbce2017-11-15 22:50:23 -080020#include "android-base/stringprintf.h"
yro98a28502018-01-18 17:00:14 -080021#include "guardrail/StatsdStats.h"
22#include "storage/StorageManager.h"
Yangster-mac330af582018-02-08 15:24:38 -080023#include "stats_log_util.h"
yro947fbce2017-11-15 22:50:23 -080024
25#include <android-base/file.h>
26#include <dirent.h>
yro98a28502018-01-18 17:00:14 -080027#include <private/android_filesystem_config.h>
28#include <fstream>
29#include <iostream>
yro947fbce2017-11-15 22:50:23 -080030
yro947fbce2017-11-15 22:50:23 -080031namespace android {
32namespace os {
33namespace statsd {
34
Yao Chenf09569f2017-12-13 17:00:51 -080035using android::util::FIELD_COUNT_REPEATED;
36using android::util::FIELD_TYPE_MESSAGE;
37using std::map;
38
yro98a28502018-01-18 17:00:14 -080039#define STATS_DATA_DIR "/data/misc/stats-data"
yro03faf092017-12-12 00:17:50 -080040#define STATS_SERVICE_DIR "/data/misc/stats-service"
Chenjie Yu6b1667c2019-01-18 10:09:33 -080041#define TRAIN_INFO_DIR "/data/misc/train-info"
yro947fbce2017-11-15 22:50:23 -080042
43// for ConfigMetricsReportList
44const int FIELD_ID_REPORTS = 2;
45
Chenjie Yu6b1667c2019-01-18 10:09:33 -080046std::mutex StorageManager::sTrainInfoMutex;
47
yro947fbce2017-11-15 22:50:23 -080048using android::base::StringPrintf;
49using std::unique_ptr;
50
yro98a28502018-01-18 17:00:14 -080051// Returns array of int64_t which contains timestamp in seconds, uid, and
52// configID.
53static void parseFileName(char* name, int64_t* result) {
54 int index = 0;
55 char* substr = strtok(name, "_");
56 while (substr != nullptr && index < 3) {
57 result[index] = StrToInt64(substr);
58 index++;
59 substr = strtok(nullptr, "_");
60 }
61 // When index ends before hitting 3, file name is corrupted. We
62 // intentionally put -1 at index 0 to indicate the error to caller.
Yao Chen5bfffb52018-06-21 16:58:51 -070063 // TODO(b/110563137): consider removing files with unexpected name format.
yro98a28502018-01-18 17:00:14 -080064 if (index < 3) {
65 result[0] = -1;
66 }
67}
68
69static string getFilePath(const char* path, int64_t timestamp, int64_t uid, int64_t configID) {
yro50d23f12018-01-25 13:52:20 -080070 return StringPrintf("%s/%lld_%d_%lld", path, (long long)timestamp, (int)uid,
yro98a28502018-01-18 17:00:14 -080071 (long long)configID);
72}
73
yro947fbce2017-11-15 22:50:23 -080074void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) {
75 int fd = open(file, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
Stefan Lafonc7bdc622017-11-27 12:54:21 -080076 if (fd == -1) {
yro947fbce2017-11-15 22:50:23 -080077 VLOG("Attempt to access %s but failed", file);
78 return;
79 }
yro98a28502018-01-18 17:00:14 -080080 trimToFit(STATS_SERVICE_DIR);
81 trimToFit(STATS_DATA_DIR);
yro947fbce2017-11-15 22:50:23 -080082
83 int result = write(fd, buffer, numBytes);
84 if (result == numBytes) {
85 VLOG("Successfully wrote %s", file);
86 } else {
87 VLOG("Failed to write %s", file);
88 }
yro98a28502018-01-18 17:00:14 -080089
90 result = fchown(fd, AID_STATSD, AID_STATSD);
91 if (result) {
92 VLOG("Failed to chown %s to statsd", file);
93 }
94
yro947fbce2017-11-15 22:50:23 -080095 close(fd);
96}
97
Muhammad Qureshif4ca8242019-03-01 09:20:15 -080098bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string& trainName,
99 int32_t status, const std::vector<uint8_t>& experimentIds) {
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800100 std::lock_guard<std::mutex> lock(sTrainInfoMutex);
101
102 deleteAllFiles(TRAIN_INFO_DIR);
103
104 string file_name = StringPrintf("%s/%lld", TRAIN_INFO_DIR, (long long)trainVersionCode);
105
106 int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
107 if (fd == -1) {
108 VLOG("Attempt to access %s but failed", file_name.c_str());
109 return false;
110 }
111
Muhammad Qureshif4ca8242019-03-01 09:20:15 -0800112 size_t result;
113
114 // Write # of bytes in trainName to file
115 const size_t trainNameSize = trainName.size();
116 const size_t trainNameSizeByteCount = sizeof(trainNameSize);
117 result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount);
118 if (result != trainNameSizeByteCount) {
119 VLOG("Failed to write %s", file_name.c_str());
120 return false;
121 }
122
123 // Write trainName to file
124 result = write(fd, trainName.c_str(), trainNameSize);
125 if (result != trainNameSize) {
126 VLOG("Failed to write %s", file_name.c_str());
127 return false;
128 }
129
130 // Write status to file
131 const size_t statusByteCount = sizeof(status);
132 result = write(fd, (uint8_t*)&status, statusByteCount);
133 if (result != statusByteCount) {
134 VLOG("Failed to write %s", file_name.c_str());
135 return false;
136 }
137
138 // Write experimentIds to file
139 result = write(fd, experimentIds.data(), experimentIds.size());
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800140 if (result == experimentIds.size()) {
141 VLOG("Successfully wrote %s", file_name.c_str());
142 } else {
143 VLOG("Failed to write %s", file_name.c_str());
144 return false;
145 }
146
147 result = fchown(fd, AID_STATSD, AID_STATSD);
148 if (result) {
149 VLOG("Failed to chown %s to statsd", file_name.c_str());
150 return false;
151 }
152
153 close(fd);
154 return true;
155}
156
Chenjie Yu97dbb202019-02-13 16:42:04 -0800157bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800158 std::lock_guard<std::mutex> lock(sTrainInfoMutex);
159
160 unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
161
162 if (dir == NULL) {
163 VLOG("Directory does not exist: %s", TRAIN_INFO_DIR);
164 return false;
165 }
166
167 dirent* de;
168 while ((de = readdir(dir.get()))) {
169 char* name = de->d_name;
170 if (name[0] == '.') {
171 continue;
172 }
173 trainInfo.trainVersionCode = StrToInt64(name);
174 string fullPath = StringPrintf("%s/%s", TRAIN_INFO_DIR, name);
175 int fd = open(fullPath.c_str(), O_RDONLY | O_CLOEXEC);
176 if (fd != -1) {
177 string str;
178 if (android::base::ReadFdToString(fd, &str)) {
179 close(fd);
Muhammad Qureshif4ca8242019-03-01 09:20:15 -0800180
181 auto it = str.begin();
182
183 // Read # of bytes taken by trainName in the file
184 size_t trainNameSize;
185 const size_t trainNameSizeByteCount = sizeof(trainNameSize);
186 std::copy_n(it, trainNameSizeByteCount, &trainNameSize);
187 it += trainNameSizeByteCount;
188
189 // Read trainName
190 std::copy_n(it, trainNameSize, std::back_inserter(trainInfo.trainName));
191 it += trainNameSize;
192
193 // Read status
194 const size_t statusByteCount = sizeof(trainInfo.status);
195 std::copy_n(it, statusByteCount, &trainInfo.status);
196 it += statusByteCount;
197
198 // Read experimentIds
199 std::copy(it, str.end(), std::back_inserter(trainInfo.experimentIds));
200
Chenjie Yu6b1667c2019-01-18 10:09:33 -0800201 VLOG("Read train info file successful: %s", fullPath.c_str());
202 return true;
203 }
204 }
205 close(fd);
206 }
207 return false;
208}
209
yro947fbce2017-11-15 22:50:23 -0800210void StorageManager::deleteFile(const char* file) {
211 if (remove(file) != 0) {
212 VLOG("Attempt to delete %s but is not found", file);
213 } else {
214 VLOG("Successfully deleted %s", file);
215 }
216}
217
218void StorageManager::deleteAllFiles(const char* path) {
219 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
220 if (dir == NULL) {
221 VLOG("Directory does not exist: %s", path);
222 return;
223 }
224
225 dirent* de;
226 while ((de = readdir(dir.get()))) {
227 char* name = de->d_name;
228 if (name[0] == '.') continue;
229 deleteFile(StringPrintf("%s/%s", path, name).c_str());
230 }
231}
232
yroe5f82922018-01-22 18:37:27 -0800233void StorageManager::deleteSuffixedFiles(const char* path, const char* suffix) {
yro947fbce2017-11-15 22:50:23 -0800234 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
235 if (dir == NULL) {
236 VLOG("Directory does not exist: %s", path);
237 return;
238 }
239
240 dirent* de;
241 while ((de = readdir(dir.get()))) {
242 char* name = de->d_name;
yroe5f82922018-01-22 18:37:27 -0800243 if (name[0] == '.') {
yro947fbce2017-11-15 22:50:23 -0800244 continue;
245 }
yroe5f82922018-01-22 18:37:27 -0800246 size_t nameLen = strlen(name);
247 size_t suffixLen = strlen(suffix);
248 if (suffixLen <= nameLen && strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
249 deleteFile(StringPrintf("%s/%s", path, name).c_str());
250 }
yro947fbce2017-11-15 22:50:23 -0800251 }
252}
253
254void StorageManager::sendBroadcast(const char* path,
255 const std::function<void(const ConfigKey&)>& sendBroadcast) {
256 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
257 if (dir == NULL) {
258 VLOG("no stats-data directory on disk");
259 return;
260 }
261
262 dirent* de;
263 while ((de = readdir(dir.get()))) {
264 char* name = de->d_name;
265 if (name[0] == '.') continue;
266 VLOG("file %s", name);
267
yro98a28502018-01-18 17:00:14 -0800268 int64_t result[3];
269 parseFileName(name, result);
270 if (result[0] == -1) continue;
271 int64_t uid = result[1];
272 int64_t configID = result[2];
yro947fbce2017-11-15 22:50:23 -0800273
yro98a28502018-01-18 17:00:14 -0800274 sendBroadcast(ConfigKey((int)uid, configID));
yro947fbce2017-11-15 22:50:23 -0800275 }
276}
277
David Chen48944902018-05-03 10:29:11 -0700278bool StorageManager::hasConfigMetricsReport(const ConfigKey& key) {
279 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
280 if (dir == NULL) {
281 VLOG("Path %s does not exist", STATS_DATA_DIR);
282 return false;
283 }
284
285 string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
286
287 dirent* de;
288 while ((de = readdir(dir.get()))) {
289 char* name = de->d_name;
290 if (name[0] == '.') continue;
291
292 size_t nameLen = strlen(name);
293 size_t suffixLen = suffix.length();
294 if (suffixLen <= nameLen &&
295 strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
296 // Check again that the file name is parseable.
297 int64_t result[3];
298 parseFileName(name, result);
299 if (result[0] == -1) continue;
300 return true;
301 }
302 }
303 return false;
304}
305
Bookatzc71d9012018-12-19 12:28:38 -0800306void StorageManager::appendConfigMetricsReport(const ConfigKey& key,
307 ProtoOutputStream* proto,
308 bool erasa_data) {
yro98a28502018-01-18 17:00:14 -0800309 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
Yao Chen72506222017-11-25 15:33:09 -0800310 if (dir == NULL) {
yro98a28502018-01-18 17:00:14 -0800311 VLOG("Path %s does not exist", STATS_DATA_DIR);
yro947fbce2017-11-15 22:50:23 -0800312 return;
313 }
314
Yao Chenfef48cb2018-03-21 14:43:14 -0700315 string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
yro4beccbe2018-03-15 19:42:05 -0700316
yro947fbce2017-11-15 22:50:23 -0800317 dirent* de;
318 while ((de = readdir(dir.get()))) {
319 char* name = de->d_name;
320 if (name[0] == '.') continue;
yro947fbce2017-11-15 22:50:23 -0800321
yro4beccbe2018-03-15 19:42:05 -0700322 size_t nameLen = strlen(name);
Yao Chenfef48cb2018-03-21 14:43:14 -0700323 size_t suffixLen = suffix.length();
yro4beccbe2018-03-15 19:42:05 -0700324 if (suffixLen <= nameLen &&
Yao Chenfef48cb2018-03-21 14:43:14 -0700325 strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
yro4beccbe2018-03-15 19:42:05 -0700326 int64_t result[3];
327 parseFileName(name, result);
328 if (result[0] == -1) continue;
329 int64_t timestamp = result[0];
330 int64_t uid = result[1];
331 int64_t configID = result[2];
332
333 string file_name = getFilePath(STATS_DATA_DIR, timestamp, uid, configID);
334 int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
335 if (fd != -1) {
336 string content;
337 if (android::base::ReadFdToString(fd, &content)) {
338 proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
339 content.c_str(), content.size());
340 }
341 close(fd);
yro947fbce2017-11-15 22:50:23 -0800342 }
yro947fbce2017-11-15 22:50:23 -0800343
Bookatzc71d9012018-12-19 12:28:38 -0800344 if (erasa_data) {
345 remove(file_name.c_str());
346 }
yro4beccbe2018-03-15 19:42:05 -0700347 }
yro947fbce2017-11-15 22:50:23 -0800348 }
349}
350
Yangster-mac932ecec2018-02-01 10:23:52 -0800351bool StorageManager::readFileToString(const char* file, string* content) {
352 int fd = open(file, O_RDONLY | O_CLOEXEC);
353 bool res = false;
354 if (fd != -1) {
355 if (android::base::ReadFdToString(fd, content)) {
356 res = true;
357 } else {
358 VLOG("Failed to read file %s\n", file);
359 }
360 close(fd);
361 }
362 return res;
363}
364
Yao Chenf09569f2017-12-13 17:00:51 -0800365void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) {
yro947fbce2017-11-15 22:50:23 -0800366 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
367 if (dir == NULL) {
368 VLOG("no default config on disk");
369 return;
370 }
yro98a28502018-01-18 17:00:14 -0800371 trimToFit(STATS_SERVICE_DIR);
yro947fbce2017-11-15 22:50:23 -0800372
373 dirent* de;
374 while ((de = readdir(dir.get()))) {
375 char* name = de->d_name;
376 if (name[0] == '.') continue;
377 VLOG("file %s", name);
378
yro98a28502018-01-18 17:00:14 -0800379 int64_t result[3];
380 parseFileName(name, result);
381 if (result[0] == -1) continue;
382 int64_t timestamp = result[0];
383 int64_t uid = result[1];
384 int64_t configID = result[2];
385 string file_name = getFilePath(STATS_SERVICE_DIR, timestamp, uid, configID);
yro947fbce2017-11-15 22:50:23 -0800386 int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
387 if (fd != -1) {
388 string content;
389 if (android::base::ReadFdToString(fd, &content)) {
390 StatsdConfig config;
391 if (config.ParseFromString(content)) {
yro6e304ec2018-01-16 21:00:30 -0800392 configsMap[ConfigKey(uid, configID)] = config;
yro98a28502018-01-18 17:00:14 -0800393 VLOG("map key uid=%lld|configID=%lld", (long long)uid, (long long)configID);
yro947fbce2017-11-15 22:50:23 -0800394 }
395 }
396 close(fd);
397 }
398 }
399}
400
Yangster-macb142cc82018-03-30 15:22:08 -0700401bool StorageManager::readConfigFromDisk(const ConfigKey& key, StatsdConfig* config) {
402 string content;
403 return config != nullptr &&
404 StorageManager::readConfigFromDisk(key, &content) && config->ParseFromString(content);
405}
406
407bool StorageManager::readConfigFromDisk(const ConfigKey& key, string* content) {
yro44907652018-03-12 20:44:05 -0700408 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR),
409 closedir);
410 if (dir == NULL) {
411 VLOG("Directory does not exist: %s", STATS_SERVICE_DIR);
412 return false;
413 }
414
Yao Chenfef48cb2018-03-21 14:43:14 -0700415 string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
yro44907652018-03-12 20:44:05 -0700416 dirent* de;
417 while ((de = readdir(dir.get()))) {
418 char* name = de->d_name;
419 if (name[0] == '.') {
420 continue;
421 }
422 size_t nameLen = strlen(name);
Yao Chenfef48cb2018-03-21 14:43:14 -0700423 size_t suffixLen = suffix.length();
yro44907652018-03-12 20:44:05 -0700424 // There can be at most one file that matches this suffix (config key).
425 if (suffixLen <= nameLen &&
Yao Chenfef48cb2018-03-21 14:43:14 -0700426 strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
yro44907652018-03-12 20:44:05 -0700427 int fd = open(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str(),
428 O_RDONLY | O_CLOEXEC);
429 if (fd != -1) {
Yangster-macb142cc82018-03-30 15:22:08 -0700430 if (android::base::ReadFdToString(fd, content)) {
431 return true;
yro44907652018-03-12 20:44:05 -0700432 }
yro4beccbe2018-03-15 19:42:05 -0700433 close(fd);
yro44907652018-03-12 20:44:05 -0700434 }
435 }
436 }
437 return false;
438}
439
Yangster-macb142cc82018-03-30 15:22:08 -0700440bool StorageManager::hasIdenticalConfig(const ConfigKey& key,
441 const vector<uint8_t>& config) {
442 string content;
443 if (StorageManager::readConfigFromDisk(key, &content)) {
444 vector<uint8_t> vec(content.begin(), content.end());
445 if (vec == config) {
446 return true;
447 }
448 }
449 return false;
450}
451
yro98a28502018-01-18 17:00:14 -0800452void StorageManager::trimToFit(const char* path) {
453 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
454 if (dir == NULL) {
455 VLOG("Path %s does not exist", path);
456 return;
457 }
458 dirent* de;
459 int totalFileSize = 0;
460 vector<string> fileNames;
461 while ((de = readdir(dir.get()))) {
462 char* name = de->d_name;
463 if (name[0] == '.') continue;
464
465 int64_t result[3];
466 parseFileName(name, result);
467 if (result[0] == -1) continue;
468 int64_t timestamp = result[0];
469 int64_t uid = result[1];
470 int64_t configID = result[2];
471 string file_name = getFilePath(path, timestamp, uid, configID);
472
473 // Check for timestamp and delete if it's too old.
Yangster-mac330af582018-02-08 15:24:38 -0800474 long fileAge = getWallClockSec() - timestamp;
yro98a28502018-01-18 17:00:14 -0800475 if (fileAge > StatsdStats::kMaxAgeSecond) {
476 deleteFile(file_name.c_str());
477 }
478
479 fileNames.push_back(file_name);
480 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
481 if (file.is_open()) {
482 file.seekg(0, ios::end);
483 int fileSize = file.tellg();
484 file.close();
485 totalFileSize += fileSize;
486 }
487 }
488
489 if (fileNames.size() > StatsdStats::kMaxFileNumber ||
490 totalFileSize > StatsdStats::kMaxFileSize) {
491 // Reverse sort to effectively remove from the back (oldest entries).
492 // This will sort files in reverse-chronological order.
493 sort(fileNames.begin(), fileNames.end(), std::greater<std::string>());
494 }
495
496 // Start removing files from oldest to be under the limit.
497 while (fileNames.size() > 0 && (fileNames.size() > StatsdStats::kMaxFileNumber ||
498 totalFileSize > StatsdStats::kMaxFileSize)) {
499 string file_name = fileNames.at(fileNames.size() - 1);
500 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
501 if (file.is_open()) {
502 file.seekg(0, ios::end);
503 int fileSize = file.tellg();
504 file.close();
505 totalFileSize -= fileSize;
506 }
507
508 deleteFile(file_name.c_str());
509 fileNames.pop_back();
510 }
511}
512
Yao Chena80e5c02018-09-04 13:55:29 -0700513void StorageManager::printStats(int outFd) {
514 printDirStats(outFd, STATS_SERVICE_DIR);
515 printDirStats(outFd, STATS_DATA_DIR);
yro665208d2018-03-13 18:08:09 -0700516}
517
Yao Chena80e5c02018-09-04 13:55:29 -0700518void StorageManager::printDirStats(int outFd, const char* path) {
519 dprintf(outFd, "Printing stats of %s\n", path);
yro665208d2018-03-13 18:08:09 -0700520 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
521 if (dir == NULL) {
522 VLOG("Path %s does not exist", path);
523 return;
524 }
525 dirent* de;
526 int fileCount = 0;
527 int totalFileSize = 0;
528 while ((de = readdir(dir.get()))) {
529 char* name = de->d_name;
530 if (name[0] == '.') {
531 continue;
532 }
533 int64_t result[3];
534 parseFileName(name, result);
535 if (result[0] == -1) continue;
536 int64_t timestamp = result[0];
537 int64_t uid = result[1];
538 int64_t configID = result[2];
Yao Chena80e5c02018-09-04 13:55:29 -0700539 dprintf(outFd, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld", fileCount + 1,
540 (long long)timestamp, (int)uid, (long long)configID);
yro665208d2018-03-13 18:08:09 -0700541 string file_name = getFilePath(path, timestamp, uid, configID);
542 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
543 if (file.is_open()) {
544 file.seekg(0, ios::end);
545 int fileSize = file.tellg();
546 file.close();
Yao Chena80e5c02018-09-04 13:55:29 -0700547 dprintf(outFd, ", File Size: %d bytes", fileSize);
yro665208d2018-03-13 18:08:09 -0700548 totalFileSize += fileSize;
549 }
Yao Chena80e5c02018-09-04 13:55:29 -0700550 dprintf(outFd, "\n");
yro665208d2018-03-13 18:08:09 -0700551 fileCount++;
552 }
Yao Chena80e5c02018-09-04 13:55:29 -0700553 dprintf(outFd, "\tTotal number of files: %d, Total size of files: %d bytes.\n", fileCount,
554 totalFileSize);
yro665208d2018-03-13 18:08:09 -0700555}
556
yro947fbce2017-11-15 22:50:23 -0800557} // namespace statsd
558} // namespace os
559} // namespace android