#include <unistd.h>
#include <linux/taskstats.h>
-#include <netlink/netlink.h>
-#include <netlink/genl/genl.h>
-#include <netlink/genl/ctrl.h>
#include <util/log.h>
#include <util/resource.h>
return total_time;
}
-static int parse_aggregate_task_stats(struct nlattr *attr, int attr_size,
- struct taskstats *stats)
-{
- nla_for_each_attr(attr, attr, attr_size, attr_size) {
- switch (attr->nla_type) {
- case TASKSTATS_TYPE_PID:
- case TASKSTATS_TYPE_TGID:
- break;
- case TASKSTATS_TYPE_STATS:
- nla_memcpy(stats, attr, sizeof(struct taskstats));
- break;
- default:
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-static int parse_task_stats(struct nl_msg *msg, void *arg)
-{
- struct genlmsghdr *hdr = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
- struct nlattr *attr = genlmsg_attrdata(hdr, 0);
- int remaining = genlmsg_attrlen(hdr, 0);
- int ret;
-
- nla_for_each_attr(attr, attr, remaining, remaining) {
- switch (attr->nla_type) {
- case TASKSTATS_TYPE_AGGR_PID:
- case TASKSTATS_TYPE_AGGR_TGID:
- ret = parse_aggregate_task_stats(nla_data(attr),
- nla_len(attr), arg);
- if (ret < 0) {
- _E("failed to parse netlink message\n");
- return NL_STOP;
- }
- break;
- default:
- break;
- }
- }
- return NL_STOP;
-}
-
-static int print_receive_error(struct sockaddr_nl *address,
- struct nlmsgerr *error, void *arg)
-{
- return NL_STOP;
-}
-
-static int query_taskstats(struct taskstats *stats, int cmd_type, pid_t pid)
-{
- struct nl_sock *sock;
- struct nl_msg *msg;
- struct nl_cb *cb;
- int ret;
-
- sock = nl_socket_alloc();
- if (!sock)
- return -ENOMEM;
-
- ret = genl_connect(sock);
- if (ret < 0)
- goto err_free_sock;
-
- ret = genl_ctrl_resolve(sock, TASKSTATS_GENL_NAME);
- if (ret < 0)
- goto err_genl_close;
-
- msg = nlmsg_alloc();
- if (!msg) {
- ret = -ENOMEM;
- goto err_genl_close;
- }
-
- genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, ret, 0, 0, TASKSTATS_CMD_GET, TASKSTATS_VERSION);
-
- ret = nla_put_u32(msg, cmd_type, pid);
- if (ret < 0)
- goto err_msg_free;
-
- ret = nl_send_auto_complete(sock, msg);
- nlmsg_free(msg);
- if (ret < 0)
- goto err_genl_close;
-
- cb = nl_cb_get(nl_cb_alloc(NL_CB_CUSTOM));
- nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, &parse_task_stats, stats);
- nl_cb_err(cb, NL_CB_CUSTOM, &print_receive_error, NULL);
-
- ret = nl_recvmsgs(sock, cb);
- nl_cb_put(cb);
- if (ret < 0)
- goto err_genl_close;
-
- nl_close(sock);
- nl_socket_free(sock);
-
- return 0;
-
-err_msg_free:
- nlmsg_free(msg);
-err_genl_close:
- nl_close(sock);
-err_free_sock:
- nl_socket_free(sock);
-
- return ret;
-}
-
-static int update_taskstats(struct process_context *ctx)
-{
- struct taskstats stats, *curr = &ctx->curr;
- struct dirent *task_entry;
- DIR *task_dir;
- char task_dir_path[BUFF_MAX];
- pid_t pid;
- int ret;
-
- memset(curr, 0, sizeof(struct taskstats));
-
- sprintf(task_dir_path, "/proc/%d/task/", ctx->tgid);
-
- task_dir = opendir(task_dir_path);
- if (!task_dir)
- return -ESRCH;
-
- while ((task_entry = readdir(task_dir)) != NULL) {
- const char *name = task_entry->d_name;
-
- if (task_entry->d_type != DT_DIR && task_entry->d_type != DT_UNKNOWN)
- continue;
-
- if (name[0] < '0' || name[0] > '9')
- continue;
-
- pid = strtoul(name, NULL, 10);
-
- ret = query_taskstats(&stats, TASKSTATS_CMD_ATTR_PID, pid);
- if (ret < 0)
- /* threads can be removed before get taskstats */
- continue;
-
- curr->ac_utime += stats.ac_utime;
- curr->ac_stime += stats.ac_stime;
- curr->ac_etime += stats.ac_etime;
- curr->virtmem += stats.virtmem;
- curr->coremem += stats.coremem;
- curr->read_bytes += stats.read_bytes;
- curr->write_bytes += stats.write_bytes;
- }
-
- closedir(task_dir);
-
- return 0;
-}
-
static int process_setup_tgid(const struct resource *res,
const struct resource_control *ctrl,
const void *data)
ctx->prev_total_time = get_total_cpu_time();
/* update initial status */
- ret = update_taskstats(ctx);
+ ret = kernel_get_process_taskstats(&ctx->curr, ctx->tgid, false);
if (ret < 0)
return ret;
memcpy(&ctx->prev, &ctx->curr, sizeof(struct taskstats));
- ret = update_taskstats(ctx);
+ ret = kernel_get_thread_group_taskstats(&ctx->curr, ctx->tgid, false);
if (ret < 0)
return ret;
#include <glib.h>
#include <stdio.h>
#include <string.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
#include <util/common.h>
#include <util/log.h>
return 0;
}
+
+static int parse_aggregate_task_stats(struct nlattr *attr, int attr_size,
+ struct taskstats *stats)
+{
+ nla_for_each_attr(attr, attr, attr_size, attr_size) {
+ switch (attr->nla_type) {
+ case TASKSTATS_TYPE_PID:
+ case TASKSTATS_TYPE_TGID:
+ break;
+ case TASKSTATS_TYPE_STATS:
+ nla_memcpy(stats, attr, sizeof(struct taskstats));
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int parse_task_stats(struct nl_msg *msg, void *arg)
+{
+ struct genlmsghdr *hdr = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *attr = genlmsg_attrdata(hdr, 0);
+ int remaining = genlmsg_attrlen(hdr, 0);
+ int ret;
+
+ nla_for_each_attr(attr, attr, remaining, remaining) {
+ switch (attr->nla_type) {
+ case TASKSTATS_TYPE_AGGR_PID:
+ case TASKSTATS_TYPE_AGGR_TGID:
+ ret = parse_aggregate_task_stats(nla_data(attr),
+ nla_len(attr), arg);
+ if (ret < 0) {
+ _E("failed to parse netlink message\n");
+ return NL_STOP;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return NL_STOP;
+}
+
+static int print_receive_error(struct sockaddr_nl *address,
+ struct nlmsgerr *error, void *arg)
+{
+ return NL_STOP;
+}
+
+int kernel_get_process_taskstats(struct taskstats *stats, int cmd_type, pid_t pid)
+{
+ struct nl_sock *sock;
+ struct nl_msg *msg;
+ struct nl_cb *cb;
+ int ret;
+
+ sock = nl_socket_alloc();
+ if (!sock)
+ return -ENOMEM;
+
+ ret = genl_connect(sock);
+ if (ret < 0)
+ goto err_free_sock;
+
+ ret = genl_ctrl_resolve(sock, TASKSTATS_GENL_NAME);
+ if (ret < 0)
+ goto err_genl_close;
+
+ msg = nlmsg_alloc();
+ if (!msg) {
+ ret = -ENOMEM;
+ goto err_genl_close;
+ }
+
+ genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, ret, 0, 0, TASKSTATS_CMD_GET, TASKSTATS_VERSION);
+
+ ret = nla_put_u32(msg, cmd_type, pid);
+ if (ret < 0)
+ goto err_msg_free;
+
+ ret = nl_send_auto_complete(sock, msg);
+ nlmsg_free(msg);
+ if (ret < 0)
+ goto err_genl_close;
+
+ cb = nl_cb_get(nl_cb_alloc(NL_CB_CUSTOM));
+ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, &parse_task_stats, stats);
+ nl_cb_err(cb, NL_CB_CUSTOM, &print_receive_error, NULL);
+
+ ret = nl_recvmsgs(sock, cb);
+ nl_cb_put(cb);
+ if (ret < 0)
+ goto err_genl_close;
+
+ nl_close(sock);
+ nl_socket_free(sock);
+
+ return 0;
+
+err_msg_free:
+ nlmsg_free(msg);
+err_genl_close:
+ nl_close(sock);
+err_free_sock:
+ nl_socket_free(sock);
+
+ return ret;
+}
+
+int kernel_get_thread_group_taskstats(struct taskstats *stats, pid_t tgid, bool get_proc_info)
+{
+ struct taskstats pid_stats;
+ struct dirent *task_entry;
+ DIR *task_dir;
+ char task_dir_path[BUFF_MAX];
+ pid_t pid;
+ int ret;
+
+ ret = kernel_get_process_taskstats(stats, TASKSTATS_CMD_ATTR_TGID, tgid);
+ if (ret < 0)
+ return -EINVAL;
+
+ sprintf(task_dir_path, "/proc/%d/task/", tgid);
+
+ task_dir = opendir(task_dir_path);
+ if (!task_dir)
+ return -ESRCH;
+
+ while ((task_entry = readdir(task_dir)) != NULL) {
+ const char *name = task_entry->d_name;
+
+ if (task_entry->d_type != DT_DIR && task_entry->d_type != DT_UNKNOWN)
+ continue;
+
+ if (name[0] < '0' || name[0] > '9')
+ continue;
+
+ pid = strtoul(name, NULL, 10);
+
+ ret = kernel_get_process_taskstats(&pid_stats, TASKSTATS_CMD_ATTR_PID, pid);
+ if (ret < 0)
+ /* threads can be removed before get taskstats */
+ continue;
+
+ if (get_proc_info && tgid == pid_stats.ac_pid) {
+ stats->ac_ppid = pid_stats.ac_ppid;
+ memcpy(stats->ac_comm, pid_stats.ac_comm, strlen(pid_stats.ac_comm) + 1);
+ }
+
+ stats->virtmem += pid_stats.virtmem;
+ stats->coremem += pid_stats.coremem;
+ stats->read_bytes += pid_stats.read_bytes;
+ stats->write_bytes += pid_stats.write_bytes;
+ }
+
+ closedir(task_dir);
+
+ return 0;
+}