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