1 /*
2 * Copyright (C) 2019 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 #include "src/trace_processor/importers/ftrace/rss_stat_tracker.h"
18
19 #include "src/trace_processor/importers/common/event_tracker.h"
20 #include "src/trace_processor/importers/common/process_tracker.h"
21 #include "src/trace_processor/types/trace_processor_context.h"
22
23 #include "protos/perfetto/trace/ftrace/kmem.pbzero.h"
24
25 namespace perfetto {
26 namespace trace_processor {
27
RssStatTracker(TraceProcessorContext * context)28 RssStatTracker::RssStatTracker(TraceProcessorContext* context)
29 : context_(context) {
30 rss_members_.emplace_back(context->storage->InternString("mem.rss.file"));
31 rss_members_.emplace_back(context->storage->InternString("mem.rss.anon"));
32 rss_members_.emplace_back(context->storage->InternString("mem.swap"));
33 rss_members_.emplace_back(context->storage->InternString("mem.rss.shmem"));
34 rss_members_.emplace_back(
35 context->storage->InternString("mem.unreclaimable"));
36 rss_members_.emplace_back(
37 context->storage->InternString("mem.unknown")); // Keep this last.
38 }
39
ParseRssStat(int64_t ts,uint32_t pid,ConstBytes blob)40 void RssStatTracker::ParseRssStat(int64_t ts, uint32_t pid, ConstBytes blob) {
41 protos::pbzero::RssStatFtraceEvent::Decoder rss(blob.data, blob.size);
42 uint32_t member = static_cast<uint32_t>(rss.member());
43 int64_t size = rss.size();
44 base::Optional<bool> curr;
45 base::Optional<int64_t> mm_id;
46 if (rss.has_curr()) {
47 curr = base::make_optional(static_cast<bool>(rss.curr()));
48 }
49 if (rss.has_mm_id()) {
50 mm_id = base::make_optional(rss.mm_id());
51 }
52 ParseRssStat(ts, pid, size, member, curr, mm_id);
53 }
54
ParseRssStat(int64_t ts,uint32_t pid,int64_t size,uint32_t member,base::Optional<bool> curr,base::Optional<int64_t> mm_id)55 void RssStatTracker::ParseRssStat(int64_t ts,
56 uint32_t pid,
57 int64_t size,
58 uint32_t member,
59 base::Optional<bool> curr,
60 base::Optional<int64_t> mm_id) {
61 const auto kRssStatUnknown = static_cast<uint32_t>(rss_members_.size()) - 1;
62 if (member >= rss_members_.size()) {
63 context_->storage->IncrementStats(stats::rss_stat_unknown_keys);
64 member = kRssStatUnknown;
65 }
66
67 if (size < 0) {
68 context_->storage->IncrementStats(stats::rss_stat_negative_size);
69 return;
70 }
71
72 base::Optional<UniqueTid> utid;
73 if (mm_id.has_value() && curr.has_value()) {
74 utid = FindUtidForMmId(*mm_id, *curr, pid);
75 } else {
76 utid = context_->process_tracker->GetOrCreateThread(pid);
77 }
78
79 if (utid) {
80 context_->event_tracker->PushProcessCounterForThread(
81 ts, static_cast<double>(size), rss_members_[member], *utid);
82 } else {
83 context_->storage->IncrementStats(stats::rss_stat_unknown_thread_for_mm_id);
84 }
85 }
86
FindUtidForMmId(int64_t mm_id,bool is_curr,uint32_t pid)87 base::Optional<UniqueTid> RssStatTracker::FindUtidForMmId(int64_t mm_id,
88 bool is_curr,
89 uint32_t pid) {
90 // If curr is true, we can just overwrite the state in the map and return
91 // the utid correspodning to |pid|.
92 if (is_curr) {
93 UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
94 mm_id_to_utid_[mm_id] = utid;
95 return utid;
96 }
97
98 // If curr is false, try and lookup the utid we previously saw for this
99 // mm id.
100 auto it = mm_id_to_utid_.find(mm_id);
101 if (it == mm_id_to_utid_.end())
102 return base::nullopt;
103
104 // If the utid in the map is the same as our current utid but curr is false,
105 // that means we are in the middle of a process changing mm structs (i.e. in
106 // the middle of a vfork + exec). Therefore, we should discard the association
107 // of this vm struct with this thread.
108 UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
109 if (it->second == utid) {
110 mm_id_to_utid_.erase(it);
111 return base::nullopt;
112 }
113
114 // Verify that the utid in the map is still alive. This can happen if an mm
115 // struct we saw in the past is about to be reused after thread but we don't
116 // know the new process that struct will be associated with.
117 if (!context_->process_tracker->IsThreadAlive(it->second)) {
118 mm_id_to_utid_.erase(it);
119 return base::nullopt;
120 }
121
122 // This case happens when a process is changing the VM of another process and
123 // we know that the utid corresponding to the target process. Just return that
124 // utid.
125 return it->second;
126 }
127
128 } // namespace trace_processor
129 } // namespace perfetto
130