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 snprintf(proc_path, sizeof(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 snprintf(proc_path, sizeof(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);
239 /* to get the best search performance, the order of items should refelect the order of /proc/meminfo */
240 num_found = get_mem_size(buffer, items, items_len, items_cnt, mem_size);
244 if (num_found < items_cnt)
247 /* free_mem = "MemFree" + "Buffers" + "Cached" + "SwapCache" - "Shmem" */
248 *free_mem = (mem_size[0] + mem_size[1] + mem_size[2] + mem_size[3] - mem_size[4]) / 1024;
253 static bool get_proc_cmdline(pid_t pid, char *cmdline)
255 assert(cmdline != NULL);
258 char cmdline_path[sizeof(PROC_CMDLINE_PATH) + MAX_DEC_SIZE(int)] = {0};
262 snprintf(cmdline_path, sizeof(cmdline_path), PROC_CMDLINE_PATH, pid);
263 fp = fopen(cmdline_path, "r");
267 if (fscanf(fp, "%s", buf) < 1) {
274 filename = strrchr(buf, '/');
275 if (filename == NULL)
278 filename = filename + 1;
280 strncpy(cmdline, filename, NAME_MAX-1);
285 static bool get_proc_filename(pid_t pid, char *process_name)
288 char buf[sizeof(PROC_STAT_PATH) + MAX_DEC_SIZE(int)];
289 char filename[PATH_MAX];
291 assert(process_name != NULL);
293 snprintf(buf, sizeof(buf), PROC_STAT_PATH, pid);
294 fp = fopen(buf, "r");
299 if (fscanf(fp, "%*s (%[^)]", filename) < 1) {
304 strncpy(process_name, filename, NAME_MAX-1);
310 API bool proc_stat_get_name_by_pid(pid_t pid, char *name)
313 assert(name != NULL);
315 if (get_proc_cmdline(pid, name))
317 else if (get_proc_filename(pid, name))
324 static void diff_system_time(proc_stat_system_time *st_diff, proc_stat_system_time *st_a, proc_stat_system_time *st_b)
326 assert(st_diff != NULL);
327 assert(st_a != NULL);
328 assert(st_b != NULL);
330 st_diff->total_time = st_a->total_time - st_b->total_time;
331 st_diff->user_time = st_a->user_time - st_b->user_time;
332 st_diff->nice_time = st_a->nice_time - st_b->nice_time;
333 st_diff->system_time = st_a->system_time - st_b->system_time;
334 st_diff->idle_time = st_a->idle_time - st_b->idle_time;
335 st_diff->iowait_time = st_a->iowait_time - st_b->iowait_time;
336 st_diff->irq_time = st_a->irq_time - st_b->irq_time;
337 st_diff->softirq_time = st_a->softirq_time - st_b->softirq_time;
340 static bool get_system_time(proc_stat_system_time *st)
346 fp = fopen("/proc/stat", "r");
350 if (fscanf(fp, "%*s %lld %lld %lld %lld %lld %lld %lld",
351 &st->user_time, &st->nice_time, &st->system_time, &st->idle_time,
352 &st->iowait_time, &st->irq_time, &st->softirq_time) < 1) {
359 st->total_time = st->user_time + st->nice_time + st->system_time + st->idle_time
360 + st->iowait_time + st->irq_time + st->softirq_time;
367 API bool proc_stat_get_system_time_diff(proc_stat_system_time *st_diff)
369 static proc_stat_system_time prev_st;
370 proc_stat_system_time cur_st;
372 assert(st_diff != NULL);
374 get_system_time(&cur_st);
376 if (prev_st.total_time == 0) {
377 memset(st_diff, 0, sizeof(proc_stat_system_time));
382 diff_system_time(st_diff, &cur_st, &prev_st);
385 return (bool) (st_diff->total_time);
389 static int comapre_pid(const pid_t *pid_a, const pid_t *pid_b)
391 assert(pid_a != NULL);
392 assert(pid_b != NULL);
394 /* the process which has smaller number of pid is ordered ahead */
395 return (*pid_a - *pid_b);
399 * @brief Get pids under /proc file system
401 * @param pids the pointer of GArray to store pids
402 * @return true on success, false on failure.
404 * This function fills Garray instance with pids under /proc file system.
407 static bool get_pids(GArray *pids)
411 struct dirent *result;
414 assert(pids != NULL);
416 dirp = opendir("/proc");
421 while (!(ret = readdir_r(dirp, &entry, &result)) && result != NULL) {
422 const char *p = entry.d_name;
427 if (*p < '0' || *p > '9')
435 pid = strtol(entry.d_name, &end, 10);
437 g_array_append_val(pids, pid);
444 g_array_sort(pids, (GCompareFunc)comapre_pid);
449 API bool proc_stat_get_pids(pid_t **pids, int *cnt)
452 GArray *garray = NULL;
454 assert(pids != NULL);
457 garray = g_array_new(false, false, sizeof(pid_t));
458 g_return_val_if_fail(garray, false);
460 if (get_pids(garray) == false) {
461 /* g_array_free is resistant to input NULL */
462 g_array_free(garray, true);
466 *pids = malloc(sizeof(pid_t) * garray->len);
467 assert(*pids != NULL);
471 for (i = 0 ; i < garray->len; ++i)
472 (*pids)[i] = g_array_index(garray, pid_t, i);
474 g_array_free(garray, true);
481 * @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
483 * @param pids GArray instance which have current pids under /proc file system
484 * @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.
485 * @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
486 ,pass NULL if if this information is not necessary
489 * 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
493 static void update_proc_infos(GArray *pids, GArray *proc_infos,
494 GArray *terminated_proc_infos)
496 /* when this function is called first, we don't have basis
497 * for time interval, so it is not valid.
499 static bool first = true;
501 unsigned int pids_cnt = 0;
502 unsigned int cur_pi_idx = 0;
503 proc_stat_process_info *pi = NULL;
507 assert(pids != NULL);
508 assert(proc_infos != NULL);
510 pids_cnt = pids->len;
512 /* with current pids, update proc_infos */
513 for (i = 0 ; i < pids_cnt; ++i) {
514 unsigned long utime, stime;
516 if (cur_pi_idx < proc_infos->len)
517 pi = &g_array_index(proc_infos, proc_stat_process_info, cur_pi_idx);
519 /* current pid is out of proc_infos, so it is new pid. */
522 assert(i < pids->len);
523 pid_t pid = g_array_index(pids, pid_t, i);
525 if ((pi != NULL) && (pi->pid == pid)) {
526 /* current pid is matched with proc_infos[curPsIdex],
527 * so update proc_infos[curPsIdex]
531 pi->fresh = false; /* it is not new process */
532 /* by now, we don't know whether it is valid or not,
533 * so mark it as invalid by default.
537 if (!(proc_stat_get_cpu_time_by_pid(pid, &utime, &stime) && proc_stat_get_mem_usage_by_pid(pid, &(pi->rss))))
540 if ((pi->utime_prev == utime) && (pi->stime_prev == stime)) {
541 /* There is no diff in execution time, mark it as inactive. */
547 pi->active = true; /* mark it as active */
549 /* update time related fields */
550 pi->utime_diff = (utime - pi->utime_prev);
551 pi->stime_diff = (stime - pi->stime_prev);
552 pi->utime_prev = utime;
553 pi->stime_prev = stime;
555 pi->valid = true; /* mark it as valid */
556 } else if ((pi == NULL) || (pi->pid > pid)) {
557 /* in case of new process */
558 proc_stat_process_info new_pi;
562 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)))
563 continue; /* in case of not getting information of current pid, skip it */
565 new_pi.fresh = true; /* mark it as new (process) */
566 new_pi.utime_prev = utime;
567 new_pi.stime_prev = stime;
568 new_pi.utime_diff = utime;
569 new_pi.stime_diff = stime;
572 /* This process is created after the first call of update_proc_infos, so we know execution time of it.
577 new_pi.valid = false;
579 /* add it to proc_infos */
580 g_array_insert_val(proc_infos, cur_pi_idx , new_pi);
583 if (terminated_proc_infos != NULL) {
584 proc_stat_process_info terminated_pi;
586 g_array_append_val(terminated_proc_infos, terminated_pi);
589 /* in case of the process terminated, remove it from proc_infos */
590 assert(cur_pi_idx < proc_infos->len);
591 g_array_remove_index(proc_infos, cur_pi_idx);
592 /* current pid should be compared again, so decrease loop count */
598 /* in case of the process terminated, remove it from proc_infos */
599 while (cur_pi_idx < proc_infos->len) {
600 if (terminated_proc_infos != NULL) {
601 proc_stat_process_info terminated_pi;
603 assert(cur_pi_idx < proc_infos->len);
604 terminated_pi = g_array_index(proc_infos, proc_stat_process_info, cur_pi_idx);
605 g_array_append_val(terminated_proc_infos, terminated_pi);
608 assert(cur_pi_idx < proc_infos->len);
609 g_array_remove_index(proc_infos, cur_pi_idx);
617 static int compare_proc_info(const proc_stat_process_info *proc_info_a, const proc_stat_process_info *proc_info_b)
620 * Firstly, long execution time process is ordered ahead
621 * Secondly, newly created process is ordered ahead
623 unsigned long exec_time_a, exec_time_b;
625 assert(proc_info_a != NULL);
626 assert(proc_info_b != NULL);
628 exec_time_a = proc_info_a->utime_diff + proc_info_a->stime_diff;
629 exec_time_b = proc_info_b->utime_diff + proc_info_b->stime_diff;
631 if (exec_time_a != exec_time_b)
632 return (exec_time_b - exec_time_a);
634 if (proc_info_a->fresh != proc_info_b->fresh)
635 return ((int)(proc_info_b->fresh) - (int)(proc_info_a->fresh));
643 * @brief Extract valid proc_stat_process_info instances from proc_infos and fill valid_proc_infos with these instances
645 * @param proc_infos from which source to extract valid proc_stat_process_info instances
646 * @param valid_proc_infos GArray instance to be filled with valid proc_stat_process_info instances
647 * @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
650 * This function extracts valid proc_stat_process_info instances from proc_infos and fills valid_proc_infos with these instances
652 static void pick_valid_proc_infos(GArray *proc_infos, GArray *valid_proc_infos, unsigned long *total_valid_proc_time)
655 proc_stat_process_info pi;
657 assert(valid_proc_infos != NULL);
659 if (total_valid_proc_time != NULL)
660 *total_valid_proc_time = 0;
662 for (i = 0; i < proc_infos->len ; ++i) {
663 assert(i < proc_infos->len);
664 pi = g_array_index(proc_infos, proc_stat_process_info, i);
667 g_array_append_val(valid_proc_infos, pi);
669 if (total_valid_proc_time != NULL)
670 *total_valid_proc_time += (pi.utime_diff+pi.stime_diff);
674 g_array_sort(valid_proc_infos, (GCompareFunc)compare_proc_info);
678 static GArray *g_pids = NULL;
679 static GArray *proc_infos = NULL;
681 API void proc_stat_init(void)
683 g_pids = g_array_new(false, false, sizeof(pid_t));
684 proc_infos = g_array_new(false, false, sizeof(proc_stat_process_info));
687 API bool proc_stat_get_process_info(GArray *valid_proc_infos,
688 GArray *terminated_proc_infos,
689 unsigned long *total_valid_proc_time)
691 assert(valid_proc_infos != NULL);
693 g_array_set_size(g_pids, 0);
695 if (!get_pids(g_pids))
698 update_proc_infos(g_pids, proc_infos, terminated_proc_infos);
699 pick_valid_proc_infos(proc_infos, valid_proc_infos, total_valid_proc_time);
705 API void proc_stat_finalize(void)
708 g_array_free(g_pids, true);
713 g_array_free(proc_infos, true);
719 API unsigned int proc_stat_get_gpu_clock(void)
724 fp = fopen("/sys/module/mali/parameters/mali_gpu_clk", "r");
728 if (fscanf(fp, "%d", &clock) < 1) {
738 bool proc_stat_is_gpu_on(void)
740 if (proc_stat_get_gpu_clock() <= 0)
748 static inline int send_int(int fd, int val)
750 return write(fd, &val, sizeof(int));
753 static inline int send_str(int fd, char *str)
759 ret = write(fd, &len, sizeof(int));
762 if (len > NOTI_MAXARGLEN)
763 len = NOTI_MAXARGLEN;
764 ret = write(fd, &len, sizeof(int));
766 _E("%s: write failed\n", __FUNCTION__);
769 ret = write(fd, str, len);
774 static int send_socket(struct resourced_noti *msg, bool sync)
779 struct sockaddr_un clientaddr;
783 client_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
784 if (client_sockfd == -1) {
785 _E("%s: socket create failed\n", __FUNCTION__);
789 bzero(&clientaddr, sizeof(clientaddr));
790 clientaddr.sun_family = AF_UNIX;
791 strncpy(clientaddr.sun_path, RESOURCED_SOCKET_PATH, sizeof(clientaddr.sun_path) - 1);
792 client_len = sizeof(clientaddr);
794 if (connect(client_sockfd, (struct sockaddr *)&clientaddr, client_len) <
796 _E("%s: connect failed\n", __FUNCTION__);
797 close(client_sockfd);
798 return RESOURCED_ERROR_FAIL;
801 send_int(client_sockfd, msg->pid);
802 send_int(client_sockfd, msg->type);
803 send_str(client_sockfd, msg->path);
804 send_int(client_sockfd, msg->argc);
805 for (i = 0; i < msg->argc; i++)
806 send_str(client_sockfd, msg->argv[i]);
809 ret = read(client_sockfd, &result, sizeof(int));
811 _E("%s: read failed\n", __FUNCTION__);
812 close(client_sockfd);
813 return RESOURCED_ERROR_FAIL;
817 close(client_sockfd);
821 static resourced_ret_c proc_cgroup_send_status(const int type, int num, ...)
823 struct resourced_noti *msg;
824 resourced_ret_c ret = RESOURCED_ERROR_NONE;
829 bool sync = SYNC_OPERATION(type);
831 msg = malloc(sizeof(struct resourced_noti));
834 return RESOURCED_ERROR_OUT_OF_MEMORY;
841 va_start(argptr, num);
842 /* it's just for debug purpose to test error reporting */
843 for (i = 0; i < num; i++) {
844 args = va_arg(argptr, char *);
849 ret = send_socket(msg, sync);
851 ret = RESOURCED_ERROR_FAIL;
858 API resourced_ret_c proc_cgroup_foregrd(void)
860 char buf[MAX_DEC_SIZE(int)];
861 snprintf(buf, sizeof(buf), "%d", getpid());
862 return proc_cgroup_send_status(PROC_CGROUP_SET_FOREGRD, 1, buf);
865 API resourced_ret_c proc_cgroup_backgrd(void)
867 char buf[MAX_DEC_SIZE(int)];
868 snprintf(buf, sizeof(buf), "%d", getpid());
869 return proc_cgroup_send_status(PROC_CGROUP_SET_BACKGRD, 1, buf);
872 API resourced_ret_c proc_cgroup_active(pid_t pid)
874 char buf[MAX_DEC_SIZE(int)];
875 snprintf(buf, sizeof(buf), "%d", pid);
876 return proc_cgroup_send_status(PROC_CGROUP_SET_ACTIVE, 1, buf);
879 API resourced_ret_c proc_cgroup_inactive(pid_t pid)
881 char buf[MAX_DEC_SIZE(int)];
882 snprintf(buf, sizeof(buf), "%d", pid);
883 return proc_cgroup_send_status(PROC_CGROUP_SET_INACTIVE, 1, buf);
886 API resourced_ret_c proc_group_change_status(int type, pid_t pid, char* app_id)
888 char pid_buf[MAX_DEC_SIZE(int)];
889 char appid_buf[NOTI_MAXARGLEN];
890 snprintf(pid_buf, sizeof(pid_buf), "%d", pid);
891 snprintf(appid_buf, sizeof(appid_buf)-1, "%s", app_id);
892 return proc_cgroup_send_status(type, 2, pid_buf, appid_buf);
895 API resourced_ret_c proc_cgroup_sweep_memory(void)
897 char buf[MAX_DEC_SIZE(int)];
898 snprintf(buf, sizeof(buf), "%d", getpid());
899 return proc_cgroup_send_status(PROC_CGROUP_GET_MEMSWEEP, 1, buf);
902 API resourced_ret_c proc_cgroup_launch(int type, pid_t pid, char* app_id, char* pkg_id)
904 char pid_buf[MAX_DEC_SIZE(int)];
905 char appid_buf[NOTI_MAXARGLEN];
906 char pkgid_buf[NOTI_MAXARGLEN];
907 snprintf(pid_buf, sizeof(pid_buf), "%d", pid);
908 snprintf(appid_buf, sizeof(appid_buf)-1, "%s", app_id);
909 snprintf(pkgid_buf, sizeof(pkgid_buf)-1, "%s", pkg_id);
910 return proc_cgroup_send_status(type, 3, pid_buf, appid_buf, pkgid_buf);