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