1 /*
2 * Copyright (C) 2022 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 <inttypes.h>
18 #include <linux/oom.h>
19 #include <stdlib.h>
20
21 #include <iostream>
22 #include <string>
23 #include <vector>
24
25 #include <android-base/file.h>
26 #include <android-base/parseint.h>
27 #include <android-base/stringprintf.h>
28 #include <android-base/strings.h>
29 #include <meminfo/procmeminfo.h>
30
31 #include <processrecord.h>
32
33 namespace android {
34 namespace smapinfo {
35
36 using ::android::base::StringPrintf;
37 using ::android::meminfo::MemUsage;
38 using ::android::meminfo::ProcMemInfo;
39 using ::android::meminfo::Vma;
40 using ::android::meminfo::VmaCallback;
41
ProcessRecord(pid_t pid,bool get_wss,uint64_t pgflags,uint64_t pgflags_mask,bool get_cmdline,bool get_oomadj,std::ostream & err)42 ProcessRecord::ProcessRecord(pid_t pid, bool get_wss, uint64_t pgflags, uint64_t pgflags_mask,
43 bool get_cmdline, bool get_oomadj, std::ostream& err)
44 : procmem_(pid, get_wss, pgflags, pgflags_mask),
45 pid_(-1),
46 oomadj_(OOM_SCORE_ADJ_MAX + 1),
47 proportional_swap_(0),
48 unique_swap_(0),
49 zswap_(0) {
50 // cmdline_ only needs to be populated if this record will be used by procrank/librank.
51 if (get_cmdline) {
52 std::string fname = StringPrintf("/proc/%d/cmdline", pid);
53 if (!::android::base::ReadFileToString(fname, &cmdline_)) {
54 err << "Failed to read cmdline from: " << fname << "\n";
55 cmdline_ = "<unknown>";
56 }
57 // We deliberately don't read the /proc/<pid>/cmdline file directly into 'cmdline_'
58 // because some processes have cmdlines that end with "0x00 0x0A 0x00",
59 // e.g. xtra-daemon, lowi-server.
60 // The .c_str() assignment takes care of trimming the cmdline at the first 0x00. This is
61 // how the original procrank worked (luckily).
62 cmdline_.resize(strlen(cmdline_.c_str()));
63
64 // If there is no cmdline (empty, not <unknown>), a kernel thread will have comm. This only
65 // matters for bug reports, which output 'SHOW MAP <pid>: <cmdline>' as section titles.
66 if (cmdline_.empty()) {
67 fname = StringPrintf("/proc/%d/comm", pid);
68 if (!::android::base::ReadFileToString(fname, &cmdline_)) {
69 err << "Failed to read comm from: " << fname << "\n";
70 }
71 // comm seems to contain a trailing '\n' that isn't present in cmdline. dumpstate
72 // surrounds kernel thread names with brackets; this behavior is maintained here.
73 if (auto pos = cmdline_.find_last_of('\n'); pos != std::string::npos) {
74 cmdline_.erase(pos);
75 }
76 cmdline_ = StringPrintf("[%s]", cmdline_.c_str());
77 }
78 }
79
80 // oomadj_ only needs to be populated if this record will be used by procrank/librank.
81 if (get_oomadj) {
82 std::string fname = StringPrintf("/proc/%d/oom_score_adj", pid);
83 std::string oom_score;
84 if (!::android::base::ReadFileToString(fname, &oom_score)) {
85 err << "Failed to read oom_score_adj file: " << fname << "\n";
86 return;
87 }
88 if (!::android::base::ParseInt(::android::base::Trim(oom_score), &oomadj_)) {
89 err << "Failed to parse oomadj from: " << fname << "\n";
90 return;
91 }
92 }
93
94 // We generally want to use Smaps() to populate procmem_'s maps before calling Wss() or
95 // Usage(), as these will fall back on the slower ReadMaps(). However, ReadMaps() must be
96 // used if page flags are inspected, as Smaps() does not have per-page granularity.
97 if (pgflags == 0 && pgflags_mask == 0) {
98 procmem_.Smaps("", true, true);
99 }
100 usage_or_wss_ = get_wss ? procmem_.Wss() : procmem_.Usage();
101 swap_offsets_ = procmem_.SwapOffsets();
102 pid_ = pid;
103 }
104
valid() const105 bool ProcessRecord::valid() const {
106 return pid_ != -1;
107 }
108
CalculateSwap(const std::vector<uint16_t> & swap_offset_array,float zram_compression_ratio)109 void ProcessRecord::CalculateSwap(const std::vector<uint16_t>& swap_offset_array,
110 float zram_compression_ratio) {
111 for (auto& off : swap_offsets_) {
112 proportional_swap_ += getpagesize() / swap_offset_array[off];
113 unique_swap_ += swap_offset_array[off] == 1 ? getpagesize() : 0;
114 zswap_ = proportional_swap_ * zram_compression_ratio;
115 }
116 // This is divided by 1024 to convert to KB.
117 proportional_swap_ /= 1024;
118 unique_swap_ /= 1024;
119 zswap_ /= 1024;
120 }
121
122 } // namespace smapinfo
123 } // namespace android
124