1 /*
2 * Copyright (C) 2020 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 // This is a header-only file because we want to use this in the ART plugin,
18 // which should not depend on any Perfetto compilation units other than the
19 // client API.
20
21 #ifndef INCLUDE_PERFETTO_PROFILING_PARSE_SMAPS_H_
22 #define INCLUDE_PERFETTO_PROFILING_PARSE_SMAPS_H_
23
24 #include <errno.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 #include <cinttypes>
31 #include <string>
32
33 namespace perfetto {
34 namespace profiling {
35
36 struct SmapsEntry {
37 int64_t size_kb = -1;
38 int64_t private_dirty_kb = -1;
39 int64_t swap_kb = -1;
40 std::string pathname;
41 };
42
43 struct SmapsParserState {
44 bool parsed_header = false;
45 SmapsEntry current_entry{};
46 };
47
48 template <typename T>
ParseSmaps(FILE * f,T callback)49 static bool ParseSmaps(FILE* f, T callback) {
50 SmapsParserState state;
51
52 size_t line_size = 1024;
53 char* line = static_cast<char*>(malloc(line_size));
54
55 for (;;) {
56 errno = 0;
57 ssize_t rd = getline(&line, &line_size, f);
58 if (rd == -1) {
59 free(line);
60 if (state.parsed_header)
61 callback(state.current_entry);
62 return errno == 0;
63 } else {
64 if (line[rd - 1] == '\n') {
65 line[rd - 1] = '\0';
66 rd--;
67 }
68 if (!ParseSmapsLine(line, static_cast<size_t>(rd), &state, callback)) {
69 free(line);
70 return false;
71 }
72 }
73 }
74 }
75
FindNthToken(const char * line,size_t n,size_t size)76 static inline const char* FindNthToken(const char* line,
77 size_t n,
78 size_t size) {
79 size_t tokens = 0;
80 bool parsing_token = false;
81 for (size_t i = 0; i < size; ++i) {
82 if (!parsing_token && line[i] != ' ') {
83 parsing_token = true;
84 if (tokens++ == n)
85 return line + static_cast<ssize_t>(i);
86 }
87 if (line[i] == ' ')
88 parsing_token = false;
89 }
90 return nullptr;
91 }
92
93 template <typename T>
ParseSmapsLine(char * line,size_t size,SmapsParserState * state,T callback)94 static bool ParseSmapsLine(char* line,
95 size_t size,
96 SmapsParserState* state,
97 T callback) {
98 char* first_token_end = static_cast<char*>(memchr(line, ' ', size));
99 if (first_token_end == nullptr || first_token_end == line)
100 return false; // Malformed.
101 bool is_header = *(first_token_end - 1) != ':';
102
103 if (is_header) {
104 if (state->parsed_header)
105 callback(state->current_entry);
106
107 state->current_entry = {};
108 const char* last_token_begin = FindNthToken(line, 5u, size);
109 if (last_token_begin)
110 state->current_entry.pathname.assign(last_token_begin);
111 state->parsed_header = true;
112 return true;
113 }
114 if (!state->parsed_header)
115 return false;
116
117 sscanf(line, "Size: %" PRId64 " kB", &state->current_entry.size_kb);
118 sscanf(line, "Swap: %" PRId64 " kB", &state->current_entry.swap_kb);
119 sscanf(line, "Private_Dirty: %" PRId64 " kB",
120 &state->current_entry.private_dirty_kb);
121 return true;
122 }
123
124 } // namespace profiling
125 } // namespace perfetto
126
127 #endif // INCLUDE_PERFETTO_PROFILING_PARSE_SMAPS_H_
128