• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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