1 // Copyright (C) 2015 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <linux/taskstats.h> 16 #include <netlink/socket.h> 17 #include <netlink/genl/ctrl.h> 18 #include <netlink/genl/genl.h> 19 20 #include <algorithm> 21 #include <memory> 22 23 #include <android-base/logging.h> 24 25 #include "taskstats.h" 26 27 TaskstatsSocket::TaskstatsSocket() 28 : nl_(nullptr, nl_socket_free), family_id_(0) { 29 } 30 31 bool TaskstatsSocket::Open() { 32 std::unique_ptr<nl_sock, decltype(&nl_socket_free)> nl( 33 nl_socket_alloc(), nl_socket_free); 34 if (!nl.get()) { 35 LOG(FATAL) << "Failed to allocate netlink socket"; 36 } 37 38 int ret = genl_connect(nl.get()); 39 if (ret < 0) { 40 LOG(FATAL) << nl_geterror(ret) << std::endl << "Unable to open netlink socket (are you root?)"; 41 } 42 43 int family_id = genl_ctrl_resolve(nl.get(), TASKSTATS_GENL_NAME); 44 if (family_id < 0) { 45 LOG(FATAL) << nl_geterror(family_id) << std::endl << "Unable to determine taskstats family id (does your kernel support taskstats?)"; 46 } 47 48 nl_ = std::move(nl); 49 family_id_ = family_id; 50 51 return true; 52 } 53 54 void TaskstatsSocket::Close() { 55 nl_.reset(); 56 } 57 58 struct TaskStatsRequest { 59 pid_t requested_pid; 60 taskstats stats; 61 }; 62 63 static pid_t ParseAggregateTaskStats(nlattr* attr, int attr_size, 64 taskstats* stats) { 65 pid_t received_pid = -1; 66 nla_for_each_attr(attr, attr, attr_size, attr_size) { 67 switch (nla_type(attr)) { 68 case TASKSTATS_TYPE_PID: 69 case TASKSTATS_TYPE_TGID: 70 received_pid = nla_get_u32(attr); 71 break; 72 case TASKSTATS_TYPE_STATS: 73 { 74 int len = static_cast<int>(sizeof(*stats)); 75 len = std::min(len, nla_len(attr)); 76 nla_memcpy(stats, attr, len); 77 return received_pid; 78 } 79 default: 80 LOG(ERROR) << "unexpected attribute inside AGGR"; 81 return -1; 82 } 83 } 84 85 return -1; 86 } 87 88 static int ParseTaskStats(nl_msg* msg, void* arg) { 89 TaskStatsRequest* taskstats_request = static_cast<TaskStatsRequest*>(arg); 90 genlmsghdr* gnlh = static_cast<genlmsghdr*>(nlmsg_data(nlmsg_hdr(msg))); 91 nlattr* attr = genlmsg_attrdata(gnlh, 0); 92 int remaining = genlmsg_attrlen(gnlh, 0); 93 94 nla_for_each_attr(attr, attr, remaining, remaining) { 95 switch (nla_type(attr)) { 96 case TASKSTATS_TYPE_AGGR_PID: 97 case TASKSTATS_TYPE_AGGR_TGID: 98 { 99 nlattr* nested_attr = static_cast<nlattr*>(nla_data(attr)); 100 taskstats stats; 101 pid_t ret; 102 103 ret = ParseAggregateTaskStats(nested_attr, nla_len(attr), &stats); 104 if (ret < 0) { 105 LOG(ERROR) << "Bad AGGR_PID contents"; 106 } else if (ret == taskstats_request->requested_pid) { 107 taskstats_request->stats = stats; 108 } else { 109 LOG(WARNING) << "got taskstats for unexpected pid " << ret << 110 " (expected " << taskstats_request->requested_pid << ", continuing..."; 111 } 112 break; 113 } 114 case TASKSTATS_TYPE_NULL: 115 break; 116 default: 117 LOG(ERROR) << "unexpected attribute in taskstats"; 118 } 119 } 120 return NL_OK; 121 } 122 123 bool TaskstatsSocket::GetStats(int pid, int type, TaskStatistics& stats) { 124 TaskStatsRequest taskstats_request = TaskStatsRequest(); 125 taskstats_request.requested_pid = pid; 126 127 std::unique_ptr<nl_msg, decltype(&nlmsg_free)> message(nlmsg_alloc(), 128 nlmsg_free); 129 130 genlmsg_put(message.get(), NL_AUTO_PID, NL_AUTO_SEQ, family_id_, 0, 0, 131 TASKSTATS_CMD_GET, TASKSTATS_VERSION); 132 nla_put_u32(message.get(), type, pid); 133 134 int result = nl_send_auto_complete(nl_.get(), message.get()); 135 if (result < 0) { 136 return false; 137 } 138 139 std::unique_ptr<nl_cb, decltype(&nl_cb_put)> callbacks( 140 nl_cb_alloc(NL_CB_DEFAULT), nl_cb_put); 141 nl_cb_set(callbacks.get(), NL_CB_VALID, NL_CB_CUSTOM, &ParseTaskStats, 142 static_cast<void*>(&taskstats_request)); 143 144 result = nl_recvmsgs(nl_.get(), callbacks.get()); 145 if (result < 0) { 146 return false; 147 } 148 nl_wait_for_ack(nl_.get()); 149 150 stats = TaskStatistics(taskstats_request.stats); 151 152 return true; 153 } 154 155 bool TaskstatsSocket::GetPidStats(int pid, TaskStatistics& stats) { 156 return GetStats(pid, TASKSTATS_CMD_ATTR_PID, stats); 157 } 158 159 bool TaskstatsSocket::GetTgidStats(int tgid, TaskStatistics& stats) { 160 bool ret = GetStats(tgid, TASKSTATS_CMD_ATTR_TGID, stats); 161 if (ret) { 162 stats.set_pid(tgid); 163 } 164 return ret; 165 } 166 167 TaskStatistics::TaskStatistics(const taskstats& taskstats_stats) { 168 comm_ = std::string(taskstats_stats.ac_comm); 169 pid_ = taskstats_stats.ac_pid; 170 171 uid_ = taskstats_stats.ac_uid; 172 gid_ = taskstats_stats.ac_gid; 173 pid_ = taskstats_stats.ac_pid; 174 ppid_ = taskstats_stats.ac_ppid; 175 176 cpu_delay_count_ = taskstats_stats.cpu_count; 177 cpu_delay_ns_ = taskstats_stats.cpu_delay_total; 178 179 block_io_delay_count_ = taskstats_stats.blkio_count; 180 block_io_delay_ns_ = taskstats_stats.blkio_delay_total; 181 182 swap_in_delay_count_ = taskstats_stats.swapin_count; 183 swap_in_delay_ns_ = taskstats_stats.swapin_delay_total; 184 185 reclaim_delay_count_ = taskstats_stats.freepages_count; 186 reclaim_delay_ns_ = taskstats_stats.freepages_delay_total; 187 188 total_delay_ns_ = 189 cpu_delay_ns_ + block_io_delay_ns_ + swap_in_delay_ns_ + reclaim_delay_ns_; 190 191 cpu_time_real_ = taskstats_stats.cpu_run_real_total; 192 cpu_time_virtual_ = taskstats_stats.cpu_run_virtual_total; 193 194 read_bytes_ = taskstats_stats.read_bytes; 195 write_bytes_ = taskstats_stats.write_bytes; 196 read_write_bytes_ = read_bytes_ + write_bytes_; 197 cancelled_write_bytes_ = taskstats_stats.cancelled_write_bytes; 198 threads_ = 1; 199 } 200 201 void TaskStatistics::AddPidToTgid(const TaskStatistics& pid_statistics) { 202 // tgid statistics already contain delay values totalled across all pids 203 // only add IO statistics 204 read_bytes_ += pid_statistics.read_bytes_; 205 write_bytes_ += pid_statistics.write_bytes_; 206 read_write_bytes_ += pid_statistics.read_write_bytes_; 207 cancelled_write_bytes_ += pid_statistics.cancelled_write_bytes_; 208 if (pid_ == pid_statistics.pid_) { 209 comm_ = pid_statistics.comm_; 210 uid_ = pid_statistics.uid_; 211 gid_ = pid_statistics.pid_; 212 ppid_ = pid_statistics.ppid_; 213 } else { 214 threads_++; 215 } 216 } 217 218 // Store new statistics and return the delta from the old statistics 219 TaskStatistics TaskStatistics::Update(const TaskStatistics& new_statistics) { 220 TaskStatistics delta = new_statistics; 221 delta.cpu_delay_count_ -= cpu_delay_count_; 222 delta.cpu_delay_ns_ -= cpu_delay_ns_; 223 delta.block_io_delay_count_ -= block_io_delay_count_; 224 delta.block_io_delay_ns_ -= block_io_delay_ns_; 225 delta.swap_in_delay_count_ -= swap_in_delay_count_; 226 delta.swap_in_delay_ns_ -= swap_in_delay_ns_; 227 delta.reclaim_delay_count_ -= reclaim_delay_count_; 228 delta.reclaim_delay_ns_ -= reclaim_delay_ns_; 229 delta.total_delay_ns_ -= total_delay_ns_; 230 delta.cpu_time_real_ -= cpu_time_real_; 231 delta.cpu_time_virtual_ -= cpu_time_virtual_; 232 delta.read_bytes_ -= read_bytes_; 233 delta.write_bytes_ -= write_bytes_; 234 delta.read_write_bytes_ -= read_write_bytes_; 235 delta.cancelled_write_bytes_ -= cancelled_write_bytes_; 236 *this = new_statistics; 237 return delta; 238 } 239