4 * Library for getting process statistics
6 * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd.
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
28 #include <linux/limits.h>
29 #include <sys/types.h>
31 #include <sys/socket.h>
39 #include "proc_stat.h"
41 #include "proc-noti.h"
44 #define PROC_STAT_PATH "/proc/%d/stat"
45 #define PROC_STATM_PATH "/proc/%d/statm"
46 #define PROC_CMDLINE_PATH "/proc/%d/cmdline"
54 printf("assertion %s %d\n", __FILE__ , __LINE__); \
60 API bool proc_stat_get_cpu_time_by_pid(pid_t pid, unsigned long *utime,
63 char proc_path[sizeof(PROC_STAT_PATH) + MAX_DEC_SIZE(int)];
66 assert(utime != NULL);
67 assert(stime != NULL);
69 sprintf(proc_path, PROC_STAT_PATH, pid);
70 fp = fopen(proc_path, "r");
74 if (fscanf(fp, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s") < 0) {
79 if (fscanf(fp, "%lu %lu", utime, stime) < 1) {
90 API bool proc_stat_get_mem_usage_by_pid(pid_t pid, unsigned int *rss)
93 char proc_path[sizeof(PROC_STATM_PATH) + MAX_DEC_SIZE(int)] = {0};
95 sprintf(proc_path, PROC_STATM_PATH, pid);
96 fp = fopen(proc_path, "r");
100 if (fscanf(fp, "%*s %d", rss) < 1) {
107 /* convert page to Kb */
113 static int get_mem_size(char *buffer, const char *const items[], const int items_len[], const int items_cnt, unsigned int mem_size[])
118 while (*p && num_found < items_cnt) {
120 while (i < items_cnt) {
121 if (strncmp(p, items[i], items_len[i] - 1) == 0) {
122 /* the format of line is like this "MemTotal: 745696 kB" */
123 /* when it finds a item, skip the name of item */
126 /* skip white space */
132 /* go to the immediate next of numerical value */
133 while (*p >= '0' && *p <= '9')
136 /* at this time, [num,p) is the value of item */
138 /* insert null to p to parse [num,p] by atoi() */
143 /* when it is the end of buffer, to avoid out of buffer */
147 mem_size[i] = atoi(num);
160 API bool proc_stat_get_total_mem_size(unsigned int *total_mem)
165 const int buffer_size = 4096;
166 static const char *const items[] = { "MemTotal:" };
167 static const int items_len[] = {sizeof("MemTotal:") };
168 unsigned int mem_size[1];
171 assert(total_mem != NULL);
173 fd = open("/proc/meminfo", O_RDONLY);
177 buffer = malloc(buffer_size);
178 assert(buffer != NULL);
180 len = read(fd, buffer, buffer_size - 1);
190 /* to get the best search performance, the order of items should refelect the order of /proc/meminfo */
191 num_found = get_mem_size(buffer, items, items_len, 1 , mem_size);
198 /* total_mem = "MemTotal" */
199 *total_mem = mem_size[0] / 1024;
205 API bool proc_stat_get_free_mem_size(unsigned int *free_mem)
209 const int buffer_size = 4096;
211 static const char *const items[] = { "MemFree:", "Buffers:", "Cached:", "SwapCached:", "Shmem:" };
212 static const int items_len[] = { sizeof("MemFree:"), sizeof("Buffers:"), sizeof("Cached:"),
213 sizeof("SwapCached:"), sizeof("Shmem:") };
214 static const int items_cnt = ARRAY_SIZE(items);
215 unsigned int mem_size[items_cnt];
218 assert(free_mem != NULL);
220 fd = open("/proc/meminfo", O_RDONLY);
225 buffer = malloc(buffer_size);
226 assert(buffer != NULL);
228 len = read(fd, buffer, buffer_size - 1);
233 /* to get the best search performance, the order of items should refelect the order of /proc/meminfo */
234 num_found = get_mem_size(buffer, items, items_len, items_cnt, mem_size);
238 if (num_found < items_cnt)
241 /* free_mem = "MemFree" + "Buffers" + "Cached" + "SwapCache" - "Shmem" */
242 *free_mem = (mem_size[0] + mem_size[1] + mem_size[2] + mem_size[3] - mem_size[4]) / 1024;
247 static bool get_proc_cmdline(pid_t pid, char *cmdline)
249 assert(cmdline != NULL);
252 char cmdline_path[sizeof(PROC_CMDLINE_PATH) + MAX_DEC_SIZE(int)] = {0};
256 sprintf(cmdline_path, PROC_CMDLINE_PATH, pid);
257 fp = fopen(cmdline_path, "r");
261 if (fscanf(fp, "%s", buf) < 1) {
268 filename = strrchr(buf, '/');
269 if (filename == NULL)
272 filename = filename + 1;
274 strncpy(cmdline, filename, NAME_MAX-1);
279 static bool get_proc_filename(pid_t pid, char *process_name)
282 char buf[sizeof(PROC_STAT_PATH) + MAX_DEC_SIZE(int)];
283 char filename[PATH_MAX];
285 assert(process_name != NULL);
287 sprintf(buf, PROC_STAT_PATH, pid);
288 fp = fopen(buf, "r");
293 if (fscanf(fp, "%*s (%[^)]", filename) < 1) {
298 strncpy(process_name, filename, NAME_MAX-1);
304 API bool proc_stat_get_name_by_pid(pid_t pid, char *name)
307 assert(name != NULL);
309 if (get_proc_cmdline(pid, name))
311 else if (get_proc_filename(pid, name))
318 static void diff_system_time(proc_stat_system_time *st_diff, proc_stat_system_time *st_a, proc_stat_system_time *st_b)
320 assert(st_diff != NULL);
321 assert(st_a != NULL);
322 assert(st_b != NULL);
324 st_diff->total_time = st_a->total_time - st_b->total_time;
325 st_diff->user_time = st_a->user_time - st_b->user_time;
326 st_diff->nice_time = st_a->nice_time - st_b->nice_time;
327 st_diff->system_time = st_a->system_time - st_b->system_time;
328 st_diff->idle_time = st_a->idle_time - st_b->idle_time;
329 st_diff->iowait_time = st_a->iowait_time - st_b->iowait_time;
330 st_diff->irq_time = st_a->irq_time - st_b->irq_time;
331 st_diff->softirq_time = st_a->softirq_time - st_b->softirq_time;
334 static bool get_system_time(proc_stat_system_time *st)
340 fp = fopen("/proc/stat", "r");
344 if (fscanf(fp, "%*s %lld %lld %lld %lld %lld %lld %lld",
345 &st->user_time, &st->nice_time, &st->system_time, &st->idle_time,
346 &st->iowait_time, &st->irq_time, &st->softirq_time) < 1) {
353 st->total_time = st->user_time + st->nice_time + st->system_time + st->idle_time
354 + st->iowait_time + st->irq_time + st->softirq_time;
361 API bool proc_stat_get_system_time_diff(proc_stat_system_time *st_diff)
363 static proc_stat_system_time prev_st;
364 proc_stat_system_time cur_st;
366 assert(st_diff != NULL);
368 get_system_time(&cur_st);
370 if (prev_st.total_time == 0) {
371 memset(st_diff, 0, sizeof(proc_stat_system_time));
376 diff_system_time(st_diff, &cur_st, &prev_st);
379 return (bool) (st_diff->total_time);
383 static int comapre_pid(const pid_t *pid_a, const pid_t *pid_b)
385 assert(pid_a != NULL);
386 assert(pid_b != NULL);
388 /* the process which has smaller number of pid is ordered ahead */
389 return (*pid_a - *pid_b);
393 * @brief Get pids under /proc file system
395 * @param pids the pointer of GArray to store pids
396 * @return true on success, false on failure.
398 * This function fills Garray instance with pids under /proc file system.
401 static bool get_pids(GArray *pids)
404 struct dirent *entry;
406 assert(pids != NULL);
408 dirp = opendir("/proc");
413 while ((entry = readdir(dirp)) != NULL) {
414 const char *p = entry->d_name;
419 if (*p < '0' || *p > '9')
427 pid = strtol(entry->d_name, &end, 10);
429 g_array_append_val(pids, pid);
433 g_array_sort(pids, (GCompareFunc)comapre_pid);
438 API bool proc_stat_get_pids(pid_t **pids, int *cnt)
441 GArray *garray = NULL;
443 assert(pids != NULL);
446 garray = g_array_new(false, false, sizeof(pid_t));
447 g_return_val_if_fail(garray, false);
449 if (get_pids(garray) == false) {
450 /* g_array_free is resistant to input NULL */
451 g_array_free(garray, true);
455 *pids = malloc(sizeof(pid_t) * garray->len);
456 assert(*pids != NULL);
460 for (i = 0 ; i < garray->len; ++i)
461 (*pids)[i] = g_array_index(garray, pid_t, i);
463 g_array_free(garray, true);
470 * @brief Fill proc_infos with proc_stat_process_info instances which have process statistics , specially time difference each process spent in user mode and system mode between two consecutive its calls
472 * @param pids GArray instance which have current pids under /proc file system
473 * @param proc_infos the pointer of GArray instance to be filled with proc_stat_process_info instances which are matched with each pid in pids.
474 * @param terminated_proc_infos the pointer of GArray instance to be filled with proc_stat_process_info instances which were terminated between two consecutive its calls
475 ,pass NULL if if this information is not necessary
478 * This function fills proc_infos with proc_stat_process_info instances which have process statistics , specially time difference each process spent in user mode and system mode between two consecutive its calls
482 static void update_proc_infos(GArray *pids, GArray *proc_infos,
483 GArray *terminated_proc_infos)
485 /* when this function is called first, we don't have basis
486 * for time interval, so it is not valid.
488 static bool first = true;
490 unsigned int pids_cnt = 0;
491 unsigned int cur_pi_idx = 0;
492 proc_stat_process_info *pi = NULL;
496 assert(pids != NULL);
497 assert(proc_infos != NULL);
499 pids_cnt = pids->len;
501 /* with current pids, update proc_infos */
502 for (i = 0 ; i < pids_cnt; ++i) {
503 unsigned long utime, stime;
505 if (cur_pi_idx < proc_infos->len)
506 pi = &g_array_index(proc_infos, proc_stat_process_info, cur_pi_idx);
508 /* current pid is out of proc_infos, so it is new pid. */
511 assert(i < pids->len);
512 pid_t pid = g_array_index(pids, pid_t, i);
514 if ((pi != NULL) && (pi->pid == pid)) {
515 /* current pid is matched with proc_infos[curPsIdex],
516 * so update proc_infos[curPsIdex]
520 pi->fresh = false; /* it is not new process */
521 /* by now, we don't know whether it is valid or not,
522 * so mark it as invalid by default.
526 if (!(proc_stat_get_cpu_time_by_pid(pid, &utime, &stime) && proc_stat_get_mem_usage_by_pid(pid, &(pi->rss))))
529 if ((pi->utime_prev == utime) && (pi->stime_prev == stime)) {
530 /* There is no diff in execution time, mark it as inactive. */
536 pi->active = true; /* mark it as active */
538 /* update time related fields */
539 pi->utime_diff = (utime - pi->utime_prev);
540 pi->stime_diff = (stime - pi->stime_prev);
541 pi->utime_prev = utime;
542 pi->stime_prev = stime;
544 pi->valid = true; /* mark it as valid */
545 } else if ((pi == NULL) || (pi->pid > pid)) {
546 /* in case of new process */
547 proc_stat_process_info new_pi;
551 if (!(proc_stat_get_name_by_pid(pid, new_pi.name) && proc_stat_get_cpu_time_by_pid(pid, &utime, &stime) && proc_stat_get_mem_usage_by_pid(pid, &new_pi.rss)))
552 continue; /* in case of not getting information of current pid, skip it */
554 new_pi.fresh = true; /* mark it as new (process) */
555 new_pi.utime_prev = utime;
556 new_pi.stime_prev = stime;
557 new_pi.utime_diff = utime;
558 new_pi.stime_diff = stime;
561 /* This process is created after the first call of update_proc_infos, so we know execution time of it.
566 new_pi.valid = false;
568 /* add it to proc_infos */
569 g_array_insert_val(proc_infos, cur_pi_idx , new_pi);
572 if (terminated_proc_infos != NULL) {
573 proc_stat_process_info terminated_pi;
575 g_array_append_val(terminated_proc_infos, terminated_pi);
578 /* in case of the process terminated, remove it from proc_infos */
579 assert(cur_pi_idx < proc_infos->len);
580 g_array_remove_index(proc_infos, cur_pi_idx);
581 /* current pid should be compared again, so decrease loop count */
587 /* in case of the process terminated, remove it from proc_infos */
588 while (cur_pi_idx < proc_infos->len) {
589 if (terminated_proc_infos != NULL) {
590 proc_stat_process_info terminated_pi;
592 assert(cur_pi_idx < proc_infos->len);
593 terminated_pi = g_array_index(proc_infos, proc_stat_process_info, cur_pi_idx);
594 g_array_append_val(terminated_proc_infos, terminated_pi);
597 assert(cur_pi_idx < proc_infos->len);
598 g_array_remove_index(proc_infos, cur_pi_idx);
606 static int compare_proc_info(const proc_stat_process_info *proc_info_a, const proc_stat_process_info *proc_info_b)
609 * Firstly, long execution time process is ordered ahead
610 * Secondly, newly created process is ordered ahead
612 unsigned long exec_time_a, exec_time_b;
614 assert(proc_info_a != NULL);
615 assert(proc_info_b != NULL);
617 exec_time_a = proc_info_a->utime_diff + proc_info_a->stime_diff;
618 exec_time_b = proc_info_b->utime_diff + proc_info_b->stime_diff;
620 if (exec_time_a != exec_time_b)
621 return (exec_time_b - exec_time_a);
623 if (proc_info_a->fresh != proc_info_b->fresh)
624 return ((int)(proc_info_b->fresh) - (int)(proc_info_a->fresh));
632 * @brief Extract valid proc_stat_process_info instances from proc_infos and fill valid_proc_infos with these instances
634 * @param proc_infos from which source to extract valid proc_stat_process_info instances
635 * @param valid_proc_infos GArray instance to be filled with valid proc_stat_process_info instances
636 * @param total_valid_proc_time to get the sum of the time spent by all valid proc_stat_process_info instance, pass NULL if if this information is not necessary
639 * This function extracts valid proc_stat_process_info instances from proc_infos and fills valid_proc_infos with these instances
641 static void pick_valid_proc_infos(GArray *proc_infos, GArray *valid_proc_infos, unsigned long *total_valid_proc_time)
644 proc_stat_process_info pi;
646 assert(valid_proc_infos != NULL);
648 if (total_valid_proc_time != NULL)
649 *total_valid_proc_time = 0;
651 for (i = 0; i < proc_infos->len ; ++i) {
652 assert(i < proc_infos->len);
653 pi = g_array_index(proc_infos, proc_stat_process_info, i);
656 g_array_append_val(valid_proc_infos, pi);
658 if (total_valid_proc_time != NULL)
659 *total_valid_proc_time += (pi.utime_diff+pi.stime_diff);
663 g_array_sort(valid_proc_infos, (GCompareFunc)compare_proc_info);
667 static GArray *g_pids = NULL;
668 static GArray *proc_infos = NULL;
670 API void proc_stat_init(void)
672 g_pids = g_array_new(false, false, sizeof(pid_t));
673 proc_infos = g_array_new(false, false, sizeof(proc_stat_process_info));
676 API bool proc_stat_get_process_info(GArray *valid_proc_infos,
677 GArray *terminated_proc_infos,
678 unsigned long *total_valid_proc_time)
680 assert(valid_proc_infos != NULL);
682 g_array_set_size(g_pids, 0);
684 if (!get_pids(g_pids))
687 update_proc_infos(g_pids, proc_infos, terminated_proc_infos);
688 pick_valid_proc_infos(proc_infos, valid_proc_infos, total_valid_proc_time);
694 API void proc_stat_finalize(void)
697 g_array_free(g_pids, true);
702 g_array_free(proc_infos, true);
708 API unsigned int proc_stat_get_gpu_clock(void)
713 fp = fopen("/sys/module/mali/parameters/mali_gpu_clk", "r");
717 if (fscanf(fp, "%d", &clock) < 1) {
727 bool proc_stat_is_gpu_on(void)
729 if (proc_stat_get_gpu_clock() <= 0)
737 static inline int send_int(int fd, int val)
739 return write(fd, &val, sizeof(int));
742 static inline int send_str(int fd, char *str)
748 ret = write(fd, &len, sizeof(int));
751 if (len > NOTI_MAXARGLEN)
752 len = NOTI_MAXARGLEN;
753 ret = write(fd, &len, sizeof(int));
755 _E("%s: write failed\n", __FUNCTION__);
758 ret = write(fd, str, len);
763 static int send_socket(struct resourced_noti *msg, bool sync)
768 struct sockaddr_un clientaddr;
772 client_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
773 if (client_sockfd == -1) {
774 _E("%s: socket create failed\n", __FUNCTION__);
778 bzero(&clientaddr, sizeof(clientaddr));
779 clientaddr.sun_family = AF_UNIX;
780 strncpy(clientaddr.sun_path, RESOURCED_SOCKET_PATH, sizeof(clientaddr.sun_path) - 1);
781 client_len = sizeof(clientaddr);
783 if (connect(client_sockfd, (struct sockaddr *)&clientaddr, client_len) <
785 _E("%s: connect failed\n", __FUNCTION__);
786 close(client_sockfd);
787 return RESOURCED_ERROR_FAIL;
790 send_int(client_sockfd, msg->pid);
791 send_int(client_sockfd, msg->type);
792 send_str(client_sockfd, msg->path);
793 send_int(client_sockfd, msg->argc);
794 for (i = 0; i < msg->argc; i++)
795 send_str(client_sockfd, msg->argv[i]);
798 ret = read(client_sockfd, &result, sizeof(int));
800 _E("%s: read failed\n", __FUNCTION__);
801 close(client_sockfd);
802 return RESOURCED_ERROR_FAIL;
806 close(client_sockfd);
810 static resourced_ret_c proc_cgroup_send_status(const int type, int num, ...)
812 struct resourced_noti *msg;
813 resourced_ret_c ret = RESOURCED_ERROR_NONE;
818 bool sync = SYNC_OPERATION(type);
820 msg = malloc(sizeof(struct resourced_noti));
823 return RESOURCED_ERROR_OUT_OF_MEMORY;
830 va_start(argptr, num);
831 /* it's just for debug purpose to test error reporting */
832 for (i = 0; i < num; i++) {
833 args = va_arg(argptr, char *);
838 ret = send_socket(msg, sync);
840 ret = RESOURCED_ERROR_FAIL;
847 API resourced_ret_c proc_cgroup_foregrd(void)
849 char buf[MAX_DEC_SIZE(int)];
850 snprintf(buf, sizeof(buf), "%d", getpid());
851 return proc_cgroup_send_status(PROC_CGROUP_SET_FOREGRD, 1, buf);
854 API resourced_ret_c proc_cgroup_backgrd(void)
856 char buf[MAX_DEC_SIZE(int)];
857 snprintf(buf, sizeof(buf), "%d", getpid());
858 return proc_cgroup_send_status(PROC_CGROUP_SET_BACKGRD, 1, buf);
861 API resourced_ret_c proc_cgroup_active(pid_t pid)
863 char buf[MAX_DEC_SIZE(int)];
864 snprintf(buf, sizeof(buf), "%d", pid);
865 return proc_cgroup_send_status(PROC_CGROUP_SET_ACTIVE, 1, buf);
868 API resourced_ret_c proc_cgroup_inactive(pid_t pid)
870 char buf[MAX_DEC_SIZE(int)];
871 snprintf(buf, sizeof(buf), "%d", pid);
872 return proc_cgroup_send_status(PROC_CGROUP_SET_INACTIVE, 1, buf);
875 API resourced_ret_c proc_group_change_status(int type, pid_t pid, char* app_id)
877 char pid_buf[MAX_DEC_SIZE(int)];
878 char appid_buf[NOTI_MAXARGLEN];
879 snprintf(pid_buf, sizeof(pid_buf), "%d", pid);
880 snprintf(appid_buf, sizeof(appid_buf)-1, "%s", app_id);
881 return proc_cgroup_send_status(type, 2, pid_buf, appid_buf);
884 API resourced_ret_c proc_cgroup_sweep_memory(void)
886 char buf[MAX_DEC_SIZE(int)];
887 snprintf(buf, sizeof(buf), "%d", getpid());
888 return proc_cgroup_send_status(PROC_CGROUP_GET_MEMSWEEP, 1, buf);
891 API resourced_ret_c proc_cgroup_launch(int type, pid_t pid, char* app_id, char* pkg_id)
893 char pid_buf[MAX_DEC_SIZE(int)];
894 char appid_buf[NOTI_MAXARGLEN];
895 char pkgid_buf[NOTI_MAXARGLEN];
896 snprintf(pid_buf, sizeof(pid_buf), "%d", pid);
897 snprintf(appid_buf, sizeof(appid_buf)-1, "%s", app_id);
898 snprintf(pkgid_buf, sizeof(pkgid_buf)-1, "%s", pkg_id);
899 return proc_cgroup_send_status(type, 3, pid_buf, appid_buf, pkgid_buf);