blob: 1f8181266b65fa8b32862d522f6746c7a702a766 [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"
yro947fbce2017-11-15 22:50:23 -080041
42// for ConfigMetricsReportList
43const int FIELD_ID_REPORTS = 2;
44
45using android::base::StringPrintf;
46using std::unique_ptr;
47
yro98a28502018-01-18 17:00:14 -080048// Returns array of int64_t which contains timestamp in seconds, uid, and
49// configID.
50static void parseFileName(char* name, int64_t* result) {
51 int index = 0;
52 char* substr = strtok(name, "_");
53 while (substr != nullptr && index < 3) {
54 result[index] = StrToInt64(substr);
55 index++;
56 substr = strtok(nullptr, "_");
57 }
58 // When index ends before hitting 3, file name is corrupted. We
59 // intentionally put -1 at index 0 to indicate the error to caller.
60 // TODO: consider removing files with unexpected name format.
61 if (index < 3) {
62 result[0] = -1;
63 }
64}
65
66static string getFilePath(const char* path, int64_t timestamp, int64_t uid, int64_t configID) {
yro50d23f12018-01-25 13:52:20 -080067 return StringPrintf("%s/%lld_%d_%lld", path, (long long)timestamp, (int)uid,
yro98a28502018-01-18 17:00:14 -080068 (long long)configID);
69}
70
yro947fbce2017-11-15 22:50:23 -080071void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) {
72 int fd = open(file, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
Stefan Lafonc7bdc622017-11-27 12:54:21 -080073 if (fd == -1) {
yro947fbce2017-11-15 22:50:23 -080074 VLOG("Attempt to access %s but failed", file);
75 return;
76 }
yro98a28502018-01-18 17:00:14 -080077 trimToFit(STATS_SERVICE_DIR);
78 trimToFit(STATS_DATA_DIR);
yro947fbce2017-11-15 22:50:23 -080079
80 int result = write(fd, buffer, numBytes);
81 if (result == numBytes) {
82 VLOG("Successfully wrote %s", file);
83 } else {
84 VLOG("Failed to write %s", file);
85 }
yro98a28502018-01-18 17:00:14 -080086
87 result = fchown(fd, AID_STATSD, AID_STATSD);
88 if (result) {
89 VLOG("Failed to chown %s to statsd", file);
90 }
91
yro947fbce2017-11-15 22:50:23 -080092 close(fd);
93}
94
95void StorageManager::deleteFile(const char* file) {
96 if (remove(file) != 0) {
97 VLOG("Attempt to delete %s but is not found", file);
98 } else {
99 VLOG("Successfully deleted %s", file);
100 }
101}
102
103void StorageManager::deleteAllFiles(const char* path) {
104 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
105 if (dir == NULL) {
106 VLOG("Directory does not exist: %s", path);
107 return;
108 }
109
110 dirent* de;
111 while ((de = readdir(dir.get()))) {
112 char* name = de->d_name;
113 if (name[0] == '.') continue;
114 deleteFile(StringPrintf("%s/%s", path, name).c_str());
115 }
116}
117
yroe5f82922018-01-22 18:37:27 -0800118void StorageManager::deleteSuffixedFiles(const char* path, const char* suffix) {
yro947fbce2017-11-15 22:50:23 -0800119 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
120 if (dir == NULL) {
121 VLOG("Directory does not exist: %s", path);
122 return;
123 }
124
125 dirent* de;
126 while ((de = readdir(dir.get()))) {
127 char* name = de->d_name;
yroe5f82922018-01-22 18:37:27 -0800128 if (name[0] == '.') {
yro947fbce2017-11-15 22:50:23 -0800129 continue;
130 }
yroe5f82922018-01-22 18:37:27 -0800131 size_t nameLen = strlen(name);
132 size_t suffixLen = strlen(suffix);
133 if (suffixLen <= nameLen && strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
134 deleteFile(StringPrintf("%s/%s", path, name).c_str());
135 }
yro947fbce2017-11-15 22:50:23 -0800136 }
137}
138
139void StorageManager::sendBroadcast(const char* path,
140 const std::function<void(const ConfigKey&)>& sendBroadcast) {
141 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
142 if (dir == NULL) {
143 VLOG("no stats-data directory on disk");
144 return;
145 }
146
147 dirent* de;
148 while ((de = readdir(dir.get()))) {
149 char* name = de->d_name;
150 if (name[0] == '.') continue;
151 VLOG("file %s", name);
152
yro98a28502018-01-18 17:00:14 -0800153 int64_t result[3];
154 parseFileName(name, result);
155 if (result[0] == -1) continue;
156 int64_t uid = result[1];
157 int64_t configID = result[2];
yro947fbce2017-11-15 22:50:23 -0800158
yro98a28502018-01-18 17:00:14 -0800159 sendBroadcast(ConfigKey((int)uid, configID));
yro947fbce2017-11-15 22:50:23 -0800160 }
161}
162
David Chen48944902018-05-03 10:29:11 -0700163bool StorageManager::hasConfigMetricsReport(const ConfigKey& key) {
164 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
165 if (dir == NULL) {
166 VLOG("Path %s does not exist", STATS_DATA_DIR);
167 return false;
168 }
169
170 string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
171
172 dirent* de;
173 while ((de = readdir(dir.get()))) {
174 char* name = de->d_name;
175 if (name[0] == '.') continue;
176
177 size_t nameLen = strlen(name);
178 size_t suffixLen = suffix.length();
179 if (suffixLen <= nameLen &&
180 strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
181 // Check again that the file name is parseable.
182 int64_t result[3];
183 parseFileName(name, result);
184 if (result[0] == -1) continue;
185 return true;
186 }
187 }
188 return false;
189}
190
yro4beccbe2018-03-15 19:42:05 -0700191void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto) {
yro98a28502018-01-18 17:00:14 -0800192 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
Yao Chen72506222017-11-25 15:33:09 -0800193 if (dir == NULL) {
yro98a28502018-01-18 17:00:14 -0800194 VLOG("Path %s does not exist", STATS_DATA_DIR);
yro947fbce2017-11-15 22:50:23 -0800195 return;
196 }
197
Yao Chenfef48cb2018-03-21 14:43:14 -0700198 string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
yro4beccbe2018-03-15 19:42:05 -0700199
yro947fbce2017-11-15 22:50:23 -0800200 dirent* de;
201 while ((de = readdir(dir.get()))) {
202 char* name = de->d_name;
203 if (name[0] == '.') continue;
yro947fbce2017-11-15 22:50:23 -0800204
yro4beccbe2018-03-15 19:42:05 -0700205 size_t nameLen = strlen(name);
Yao Chenfef48cb2018-03-21 14:43:14 -0700206 size_t suffixLen = suffix.length();
yro4beccbe2018-03-15 19:42:05 -0700207 if (suffixLen <= nameLen &&
Yao Chenfef48cb2018-03-21 14:43:14 -0700208 strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
yro4beccbe2018-03-15 19:42:05 -0700209 int64_t result[3];
210 parseFileName(name, result);
211 if (result[0] == -1) continue;
212 int64_t timestamp = result[0];
213 int64_t uid = result[1];
214 int64_t configID = result[2];
215
216 string file_name = getFilePath(STATS_DATA_DIR, timestamp, uid, configID);
217 int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
218 if (fd != -1) {
219 string content;
220 if (android::base::ReadFdToString(fd, &content)) {
221 proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
222 content.c_str(), content.size());
223 }
224 close(fd);
yro947fbce2017-11-15 22:50:23 -0800225 }
yro947fbce2017-11-15 22:50:23 -0800226
yro4beccbe2018-03-15 19:42:05 -0700227 // Remove file from disk after reading.
228 remove(file_name.c_str());
229 }
yro947fbce2017-11-15 22:50:23 -0800230 }
231}
232
Yangster-mac932ecec2018-02-01 10:23:52 -0800233bool StorageManager::readFileToString(const char* file, string* content) {
234 int fd = open(file, O_RDONLY | O_CLOEXEC);
235 bool res = false;
236 if (fd != -1) {
237 if (android::base::ReadFdToString(fd, content)) {
238 res = true;
239 } else {
240 VLOG("Failed to read file %s\n", file);
241 }
242 close(fd);
243 }
244 return res;
245}
246
Yao Chenf09569f2017-12-13 17:00:51 -0800247void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) {
yro947fbce2017-11-15 22:50:23 -0800248 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
249 if (dir == NULL) {
250 VLOG("no default config on disk");
251 return;
252 }
yro98a28502018-01-18 17:00:14 -0800253 trimToFit(STATS_SERVICE_DIR);
yro947fbce2017-11-15 22:50:23 -0800254
255 dirent* de;
256 while ((de = readdir(dir.get()))) {
257 char* name = de->d_name;
258 if (name[0] == '.') continue;
259 VLOG("file %s", name);
260
yro98a28502018-01-18 17:00:14 -0800261 int64_t result[3];
262 parseFileName(name, result);
263 if (result[0] == -1) continue;
264 int64_t timestamp = result[0];
265 int64_t uid = result[1];
266 int64_t configID = result[2];
267 string file_name = getFilePath(STATS_SERVICE_DIR, timestamp, uid, configID);
yro947fbce2017-11-15 22:50:23 -0800268 int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
269 if (fd != -1) {
270 string content;
271 if (android::base::ReadFdToString(fd, &content)) {
272 StatsdConfig config;
273 if (config.ParseFromString(content)) {
yro6e304ec2018-01-16 21:00:30 -0800274 configsMap[ConfigKey(uid, configID)] = config;
yro98a28502018-01-18 17:00:14 -0800275 VLOG("map key uid=%lld|configID=%lld", (long long)uid, (long long)configID);
yro947fbce2017-11-15 22:50:23 -0800276 }
277 }
278 close(fd);
279 }
280 }
281}
282
Yangster-macb142cc82018-03-30 15:22:08 -0700283bool StorageManager::readConfigFromDisk(const ConfigKey& key, StatsdConfig* config) {
284 string content;
285 return config != nullptr &&
286 StorageManager::readConfigFromDisk(key, &content) && config->ParseFromString(content);
287}
288
289bool StorageManager::readConfigFromDisk(const ConfigKey& key, string* content) {
yro44907652018-03-12 20:44:05 -0700290 unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR),
291 closedir);
292 if (dir == NULL) {
293 VLOG("Directory does not exist: %s", STATS_SERVICE_DIR);
294 return false;
295 }
296
Yao Chenfef48cb2018-03-21 14:43:14 -0700297 string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
yro44907652018-03-12 20:44:05 -0700298 dirent* de;
299 while ((de = readdir(dir.get()))) {
300 char* name = de->d_name;
301 if (name[0] == '.') {
302 continue;
303 }
304 size_t nameLen = strlen(name);
Yao Chenfef48cb2018-03-21 14:43:14 -0700305 size_t suffixLen = suffix.length();
yro44907652018-03-12 20:44:05 -0700306 // There can be at most one file that matches this suffix (config key).
307 if (suffixLen <= nameLen &&
Yao Chenfef48cb2018-03-21 14:43:14 -0700308 strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
yro44907652018-03-12 20:44:05 -0700309 int fd = open(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str(),
310 O_RDONLY | O_CLOEXEC);
311 if (fd != -1) {
Yangster-macb142cc82018-03-30 15:22:08 -0700312 if (android::base::ReadFdToString(fd, content)) {
313 return true;
yro44907652018-03-12 20:44:05 -0700314 }
yro4beccbe2018-03-15 19:42:05 -0700315 close(fd);
yro44907652018-03-12 20:44:05 -0700316 }
317 }
318 }
319 return false;
320}
321
Yangster-macb142cc82018-03-30 15:22:08 -0700322bool StorageManager::hasIdenticalConfig(const ConfigKey& key,
323 const vector<uint8_t>& config) {
324 string content;
325 if (StorageManager::readConfigFromDisk(key, &content)) {
326 vector<uint8_t> vec(content.begin(), content.end());
327 if (vec == config) {
328 return true;
329 }
330 }
331 return false;
332}
333
yro98a28502018-01-18 17:00:14 -0800334void StorageManager::trimToFit(const char* path) {
335 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
336 if (dir == NULL) {
337 VLOG("Path %s does not exist", path);
338 return;
339 }
340 dirent* de;
341 int totalFileSize = 0;
342 vector<string> fileNames;
343 while ((de = readdir(dir.get()))) {
344 char* name = de->d_name;
345 if (name[0] == '.') continue;
346
347 int64_t result[3];
348 parseFileName(name, result);
349 if (result[0] == -1) continue;
350 int64_t timestamp = result[0];
351 int64_t uid = result[1];
352 int64_t configID = result[2];
353 string file_name = getFilePath(path, timestamp, uid, configID);
354
355 // Check for timestamp and delete if it's too old.
Yangster-mac330af582018-02-08 15:24:38 -0800356 long fileAge = getWallClockSec() - timestamp;
yro98a28502018-01-18 17:00:14 -0800357 if (fileAge > StatsdStats::kMaxAgeSecond) {
358 deleteFile(file_name.c_str());
359 }
360
361 fileNames.push_back(file_name);
362 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
363 if (file.is_open()) {
364 file.seekg(0, ios::end);
365 int fileSize = file.tellg();
366 file.close();
367 totalFileSize += fileSize;
368 }
369 }
370
371 if (fileNames.size() > StatsdStats::kMaxFileNumber ||
372 totalFileSize > StatsdStats::kMaxFileSize) {
373 // Reverse sort to effectively remove from the back (oldest entries).
374 // This will sort files in reverse-chronological order.
375 sort(fileNames.begin(), fileNames.end(), std::greater<std::string>());
376 }
377
378 // Start removing files from oldest to be under the limit.
379 while (fileNames.size() > 0 && (fileNames.size() > StatsdStats::kMaxFileNumber ||
380 totalFileSize > StatsdStats::kMaxFileSize)) {
381 string file_name = fileNames.at(fileNames.size() - 1);
382 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
383 if (file.is_open()) {
384 file.seekg(0, ios::end);
385 int fileSize = file.tellg();
386 file.close();
387 totalFileSize -= fileSize;
388 }
389
390 deleteFile(file_name.c_str());
391 fileNames.pop_back();
392 }
393}
394
yro665208d2018-03-13 18:08:09 -0700395void StorageManager::printStats(FILE* out) {
396 printDirStats(out, STATS_SERVICE_DIR);
397 printDirStats(out, STATS_DATA_DIR);
398}
399
400void StorageManager::printDirStats(FILE* out, const char* path) {
401 fprintf(out, "Printing stats of %s\n", path);
402 unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
403 if (dir == NULL) {
404 VLOG("Path %s does not exist", path);
405 return;
406 }
407 dirent* de;
408 int fileCount = 0;
409 int totalFileSize = 0;
410 while ((de = readdir(dir.get()))) {
411 char* name = de->d_name;
412 if (name[0] == '.') {
413 continue;
414 }
415 int64_t result[3];
416 parseFileName(name, result);
417 if (result[0] == -1) continue;
418 int64_t timestamp = result[0];
419 int64_t uid = result[1];
420 int64_t configID = result[2];
421 fprintf(out, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld",
422 fileCount + 1,
423 (long long)timestamp,
424 (int)uid,
425 (long long)configID);
426 string file_name = getFilePath(path, timestamp, uid, configID);
427 ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
428 if (file.is_open()) {
429 file.seekg(0, ios::end);
430 int fileSize = file.tellg();
431 file.close();
432 fprintf(out, ", File Size: %d bytes", fileSize);
433 totalFileSize += fileSize;
434 }
435 fprintf(out, "\n");
436 fileCount++;
437 }
438 fprintf(out, "\tTotal number of files: %d, Total size of files: %d bytes.\n",
439 fileCount, totalFileSize);
440}
441
yro947fbce2017-11-15 22:50:23 -0800442} // namespace statsd
443} // namespace os
444} // namespace android