14 #include "logd-taskstats.h"
16 #include "nlproc-stat.h"
17 #include "proc-events.h"
18 #include "proc-stat.h"
19 #include "task-stats.h"
21 #define LOGD_INTERNAL_PID 3
25 float utime_power_cons, stime_power_cons;
30 static uint64_t power_const_per_uah;
32 static int task_stats_sock;
33 static int update_task_stats_sock;
34 static int proc_events_sock;
36 static Eina_Hash *active_pids;
37 static Eina_Hash *terminated_stats;
38 static Eina_Hash *total_stats;
40 static struct process_stat* find_or_create_statistic(Eina_Hash *table, const char *cmdline);
41 static int proc_state_update(void);
43 const char *kernel_threads_name = "[kernel_threads]";
45 static int day_of_week()
50 struct tm *curr_tm = NULL;
52 curr_time = time(NULL);
53 if (curr_time == ((time_t) -1)) {
54 _E("time failed: %s", strerror(errno));
57 curr_tm = localtime(&curr_time);
58 if (curr_tm == NULL) {
60 _E("time failed: %s", strerror(errno));
64 return curr_tm->tm_wday;
67 static Eina_Bool sub_active_from_term_cb(const Eina_Hash *hash, const void *key,
68 void *data, void *fdata)
70 struct process_stat *statistic;
71 struct process_stat *active_statistic = (struct process_stat *)data;
72 char *cmdline = (char*)key;
74 statistic = find_or_create_statistic(terminated_stats, cmdline);
76 _E("find_or_create_statistic failed");
77 return 1; /* continue to process */
80 statistic->utime -= active_statistic->utime;
81 statistic->stime -= active_statistic->stime;
82 statistic->utime_power_cons -= active_statistic->utime_power_cons;
83 statistic->stime_power_cons -= active_statistic->stime_power_cons;
84 statistic->duration -= active_statistic->duration;
86 return 1; /* continue to process */
95 _I("delete_old_proc start");
97 if (nlproc_stat_store() < 0)
98 _E("nlproc_stat_store failed");
100 /* Already stored into database, so have no reason to keep it */
101 eina_hash_free_buckets(terminated_stats);
103 if (!terminated_stats) {
108 /* we have to clear next day stat due to run this function at 23:59:50 */
109 next_day = (day_of_week() + 1) % DAYS_PER_WEEK; /* next day */
111 _E("day_of_week failed");
116 ret = delete_old_power_cons(next_day);
118 _E("delete_old_power_cons failed");
121 /* It's need to avoid double counting the same data that was
122 already stored into db */
124 eina_hash_foreach(total_stats, sub_active_from_term_cb, NULL);
131 int get_task_stats_sock(void)
133 return task_stats_sock;
136 int get_proc_events_sock(void)
138 return proc_events_sock;
141 static int get_cpu_mask(char **mask)
147 cpus_number = sysconf(_SC_NPROCESSORS_CONF);
150 _E("sysconf failed: %s", strerror(errno));
154 if (asprintf(mask, "0-%d", cpus_number - 1) == -1) {
156 _E("asprintf failed: %s", strerror(errno));
163 static int get_pids_from_proc(Eina_List **pids)
165 struct dirent *entry = NULL;
169 dir = opendir("/proc");
172 _E("opendir failed: %s", strerror(errno));
176 while ((entry = readdir(dir)) != NULL) {
177 const char *p = entry->d_name;
178 char buf[30] = { 0, };
188 pid = atoi(entry->d_name);
189 sprintf(buf, "/proc/%d/stat", pid);
190 fp = fopen(buf, "r");
192 _E("fopen failed %s: %s", buf, strerror(errno));
196 ret = fscanf(fp, "%*s %*s %c", &state);
198 _E("fscanf failed: %s", strerror(errno));
201 if (fclose(fp) != 0) {
202 _E("fclose failed: %s", strerror(errno));
205 if (ret == 1 && state != 'Z') {
206 *pids = eina_list_append(*pids, (void*)pid);
207 if (eina_error_get()) {
208 _E("eina_list_append failed: %s", eina_error_msg_get(eina_error_get()));
213 if (closedir(dir) < 0) {
215 _E("closedir failed: %s", strerror(errno));
222 static int get_cmdline_by_pid(pid_t pid, char *cmdline)
226 char buf[PATH_MAX + 1] = { 0, }; /* "+1" for readlink */
229 ret = snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
230 if (ret < 0 || ret == sizeof(buf)) {
231 _E("snprintf failed or output was truncated");
234 fp = fopen(path, "r");
236 if (fscanf(fp, "%s", buf) != 1) {
239 if (fclose(fp) != 0) {
240 _E("fclose failed: %s", strerror(errno));
243 strcpy(cmdline, buf);
248 bzero(path, sizeof(path));
249 ret = snprintf(path, sizeof(path), "/proc/%d/exe", pid);
250 if (ret < 0 || ret == sizeof(path)) {
251 _E("snprintf failed or output was truncated");
255 bzero(buf, sizeof(buf));
256 if (readlink(path, buf, sizeof(buf) - 1) < 0) {
257 ret = snprintf(path, sizeof(path), "/proc/%d/comm", pid);
258 if (ret < 0 || ret == sizeof(path)) {
259 _E("snprintf failed or output was truncated");
262 if (access(path, R_OK) < 0) {
266 strcpy(cmdline, kernel_threads_name);
270 strcpy(cmdline, buf);
275 static struct process_stat* find_or_create_statistic(Eina_Hash *table, const char *cmdline)
277 struct process_stat* statistic =
278 eina_hash_find(table, cmdline);
281 statistic = (struct process_stat *)
282 calloc(1, sizeof(struct process_stat));
284 _E("calloc failed: %s", strerror(errno));
288 if (eina_hash_add(table, cmdline,
289 statistic) == EINA_FALSE) {
290 _E("eina_hash_add failed: %s", eina_error_msg_get(eina_error_get()));
291 free((void*)cmdline);
300 int proc_terminated(struct taskstats *t, void *user_data)
302 pid_t pid = t->ac_pid;
303 uint64_t s_time = t->ac_stime;
304 uint64_t u_time = t->ac_utime;
305 float u_time_power_cons = (float)t->ac_utime_power_cons / power_const_per_uah;
306 float s_time_power_cons = (float)t->ac_stime_power_cons / power_const_per_uah;
308 if (t->ac_stime || t->ac_utime) {
309 struct process_stat *statistic;
310 const char *cmdline = eina_hash_find(active_pids, &pid);
314 statistic = find_or_create_statistic(terminated_stats, cmdline);
316 _E("find_or_create_statistic failed");
320 statistic->utime += u_time;
321 statistic->stime += s_time;
322 statistic->utime_power_cons += u_time_power_cons;
323 statistic->stime_power_cons += s_time_power_cons;
324 statistic->duration += t->ac_etime / USEC_PER_SEC;
330 static enum logd_db_query load_stat_cb(const struct proc_power_cons *pc, void *user_data)
332 struct process_stat *statistic;
334 _E("Wrong hash table");
335 return LOGD_DB_QUERY_STOP;
338 statistic = find_or_create_statistic((Eina_Hash *)user_data, pc->appid);
340 _E("find_or_create_statistic failed");
344 /* TODO: remove useless utime, stime - need only power consumption */
345 statistic->utime_power_cons += pc->power_cons;
346 statistic->duration += pc->duration;
348 return LOGD_DB_QUERY_CONTINUE;
351 int nlproc_stat_init(void)
353 char *cpus_mask = NULL;
354 Eina_List *pids = NULL;
360 power_const_per_uah = config_get_int("power_const_per_uah", 3213253205ll, NULL);
362 task_stats_sock = create_netlink_socket(NETLINK_GENERIC, 0, 0);
363 if (task_stats_sock < 0) {
364 _E("create_netlink_sock failed (task_stats_sock)");
365 return task_stats_sock;
368 update_task_stats_sock = create_netlink_socket(NETLINK_GENERIC, 0, 0);
369 if (update_task_stats_sock < 0) {
370 _E("create_netlink_sock failed (update_task_stats_sock)");
371 return update_task_stats_sock;
374 ret = get_cpu_mask(&cpus_mask);
376 _E("get_cpu_mask failed");
380 ret = reg_task_stats_cpu_mask(task_stats_sock, cpus_mask);
382 _E("reg_task_stats_cpu_mask failed");
386 proc_events_sock = create_netlink_socket(NETLINK_CONNECTOR, CN_IDX_PROC, getpid());
387 if (proc_events_sock < 0) {
388 _E("create_netlink_sock failed (proc_events_sock)");
389 return proc_events_sock;
392 ret = subscribe_on_proc_events(proc_events_sock);
394 _E("subscribe_on_proc_events failed");
398 ret = get_pids_from_proc(&pids);
400 _E("get_pids_from_proc failed");
404 active_pids = eina_hash_int32_new(free);
406 _E("eina_hash_pointer_new failed");
410 EINA_LIST_FOREACH(pids, l, pid) {
411 char cmdline[PATH_MAX];
414 ret = get_cmdline_by_pid((int)pid, cmdline);
419 tmp = strdup(cmdline);
421 _E("strdup failed: %s", strerror(errno));
424 if (eina_hash_add(active_pids, &pid, tmp) == EINA_FALSE) {
425 _E("eina_hash_add failed: %s", eina_error_msg_get(eina_error_get()));
430 eina_list_free(pids);
432 terminated_stats = eina_hash_string_superfast_new(free);
433 if (!terminated_stats) {
434 _E("eina_hash_string_superfast_new failed");
441 _E("day_of_week failed");
445 total_stats = eina_hash_string_superfast_new(free);
447 _E("eina_hash_string_superfast_new failed");
451 /* ignore statistic before logd started */
453 eina_hash_foreach(total_stats, sub_active_from_term_cb, NULL);
455 if (foreach_proc_power_cons(load_stat_cb, day, terminated_stats) < 0)
456 _E("foreach_proc_power_cons failed");
462 int proc_forked(struct proc_event *e, void *user_data)
465 char cmdline[PATH_MAX];
470 if (e->what == PROC_EVENT_FORK) {
471 pid = e->event_data.fork.child_pid;
472 } else if (e->what == PROC_EVENT_EXEC) {
473 pid = e->event_data.exec.process_pid;
474 } else if (e->what == PROC_EVENT_EXIT) {
475 pid = e->event_data.exit.process_pid;
476 if (eina_hash_find(active_pids, &pid)) {
477 if (eina_hash_del(active_pids, &pid, NULL) == EINA_FALSE) {
478 _E("eina_hash_del failed %d", pid);
486 ret = get_cmdline_by_pid(pid, cmdline);
491 tmp = strdup(cmdline);
493 _E("strdup failed: %s", strerror(errno));
497 old_cmdline = eina_hash_set(active_pids, &pid, tmp);
500 else if (eina_error_get()) {
501 _E("eina_hash_set failed: %s", eina_error_msg_get(eina_error_get()));
508 int add_active_proc(struct taskstats *t, void *user_data)
510 const char *launchpad_cmdline = "/usr/bin/launchpad_preloading_preinitializing_daemon";
512 pid_t pid = t->ac_pid;
513 uint64_t s_time = t->ac_stime;
514 uint64_t u_time = t->ac_utime;
515 float u_time_power_cons = (float)t->ac_utime_power_cons / power_const_per_uah;
516 float s_time_power_cons = (float)t->ac_stime_power_cons / power_const_per_uah;
518 if (t->ac_stime || t->ac_utime) {
519 char *cmdline = eina_hash_find(active_pids, &pid);
520 struct process_stat *statistic;
524 ret = get_cmdline_by_pid(pid, buf);
528 cmdline = strdup(buf);
530 _E("strdup failed: %s", strerror(errno));
534 if (eina_hash_add(active_pids, &pid, cmdline) == EINA_FALSE) {
535 _E("eina_hash_add failed: %s", eina_error_msg_get(eina_error_get()));
536 free((void*)cmdline);
540 if (strcmp(cmdline, launchpad_cmdline) == 0) {
541 char *old_cmdline = NULL;
542 ret = get_cmdline_by_pid(pid, buf);
546 cmdline = strdup(buf);
548 _E("strdup failed: %s", strerror(errno));
551 old_cmdline = eina_hash_modify(active_pids,
554 _E("eina_hash_modify failed: %s", eina_error_msg_get(eina_error_get()));
561 statistic = find_or_create_statistic(total_stats, cmdline);
563 _E("find_or_create_statistic failed");
567 statistic->utime += u_time;
568 statistic->stime += s_time;
569 statistic->utime_power_cons += u_time_power_cons;
570 statistic->stime_power_cons += s_time_power_cons;
571 statistic->duration += t->ac_etime / USEC_PER_SEC;
572 statistic->is_active = 1;
578 static Eina_Bool update_total_stat_cb(const Eina_Hash *hash, const void *key,
579 void *data, void *fdata)
581 struct process_stat *statistic;
582 struct process_stat *new_statistic = (struct process_stat *)data;
583 char *cmdline = (char*)key;
585 statistic = find_or_create_statistic(total_stats, cmdline);
587 _E("find_or_create_statistic failed");
588 return 1; /* continue to process */
591 statistic->utime += new_statistic->utime;
592 statistic->stime += new_statistic->stime;
593 statistic->utime_power_cons += new_statistic->utime_power_cons;
594 statistic->stime_power_cons += new_statistic->stime_power_cons;
595 statistic->duration += new_statistic->duration;
597 return 1; /* continue to process */
600 Eina_Bool update_active_pids_cb(const Eina_Hash *hash, const void *key,
601 void *data, void *fdata)
603 int pid = *(int*)key;
606 ret = request_stat_by_pid(update_task_stats_sock, (int)pid);
608 _E("request_stat_by_pid failed (pid=%d)", (int)pid);
609 return 1; /* continue to process */
611 ret = process_task_stats_answer(update_task_stats_sock,
612 add_active_proc, NULL);
614 _E("process_task_stats_answer failed (pid=%d)", (int)pid);
615 return 1; /* continue to process */
618 return 1; /* continue to process */
621 static int proc_state_update(void)
623 eina_hash_free_buckets(total_stats);
625 eina_hash_foreach(active_pids, update_active_pids_cb, NULL);
626 eina_hash_foreach(terminated_stats, update_total_stat_cb, NULL);
631 static int send_one_stat(const struct logd_proc_stat *statistics, int sock)
633 const char *cmdline = (const char *)statistics->application;
634 int len = strlen(cmdline);
637 if (send(sock, (void*)&len, sizeof(len), 0) < 0) {
640 if (send(sock, cmdline, len, 0) < 0) {
643 if (send(sock, &statistics->utime, sizeof(statistics->utime), 0) < 0) {
646 if (send(sock, &statistics->stime, sizeof(statistics->stime), 0) < 0) {
649 if (send(sock, &statistics->utime_power_cons,
650 sizeof(statistics->utime_power_cons), 0) < 0) {
653 if (send(sock, &statistics->stime_power_cons,
654 sizeof(statistics->stime_power_cons), 0) < 0) {
657 if (send(sock, &statistics->is_active,
658 sizeof(statistics->is_active), 0) < 0) {
666 _E("send failed: %s", strerror(errno));
670 static int sort_stat_cb(void *d1, void *d2)
672 const struct logd_proc_stat *s1 = d1;
673 const struct logd_proc_stat *s2 = d2;
678 if (s1->utime_power_cons + s1->stime_power_cons <
679 s2->utime_power_cons + s2->stime_power_cons)
681 else if (s1->utime_power_cons + s1->stime_power_cons >
682 s2->utime_power_cons + s2->stime_power_cons)
687 int send_proc_stat(int sock)
689 int count = eina_hash_population(total_stats);
690 int day = day_of_week();
691 Eina_List *sorted_stat = NULL;
698 ret = proc_state_update();
700 _E("proc_stat_update failed");
704 for (i = 0; i < DAYS_PER_WEEK; ++i) {
705 /* stat for current day stored in activ/terminated hash tables */
708 if (foreach_proc_power_cons(load_stat_cb, i, total_stats) < 0)
709 _E("foreach_proc_power_cons failed");
712 it = eina_hash_iterator_tuple_new(total_stats);
714 _E("eina_hash_iterator_tuple_new failed");
718 while (eina_iterator_next(it, &data)) {
719 Eina_Hash_Tuple *t = data;
720 char *cmdline = (char*)t->key;
721 const struct process_stat *statistic = t->data;
722 struct logd_proc_stat *ps =
723 (struct logd_proc_stat*) malloc(sizeof(struct logd_proc_stat));
727 _E("malloc failed: %s", strerror(errno));
730 ps->application = cmdline;
731 ps->utime = statistic->utime;
732 ps->stime = statistic->stime;
733 ps->utime_power_cons = statistic->utime_power_cons;
734 ps->stime_power_cons = statistic->stime_power_cons;
735 ps->is_active = statistic->is_active;
737 sorted_stat = eina_list_sorted_insert(sorted_stat,
738 EINA_COMPARE_CB(sort_stat_cb), ps);
739 if (eina_error_get()) {
740 _E("eina_list_sorted_insert failed: %s", eina_error_msg_get(eina_error_get()));
745 if (send(sock, &count, sizeof(count), 0) < 0) {
747 _E("send failed: %s", strerror(errno));
751 EINA_LIST_FOREACH(sorted_stat, l, data) {
752 ret = send_one_stat(data, sock);
755 _E("send_one_stat failed");
760 EINA_LIST_FOREACH(sorted_stat, l, data) {
764 eina_iterator_free(it);
766 eina_list_free(sorted_stat);
772 EINA_LIST_FOREACH(sorted_stat, l, data) {
778 eina_iterator_free(it);
781 eina_list_free(sorted_stat);
785 int nlproc_stat_store(void)
792 _I("nlproc_stat_store start");
794 ret = proc_state_update();
796 _E("proc_state_update failed");
800 it = eina_hash_iterator_tuple_new(total_stats);
802 _E("eina_hash_iterator_tuple_new failed");
809 _E("day_of_week failed");
813 while (eina_iterator_next(it, &data)) {
814 struct proc_power_cons pc;
815 Eina_Hash_Tuple *t = data;
816 const char *cmdline = t->key;
817 const struct process_stat *statistic = t->data;
820 pc.power_cons = statistic->stime_power_cons + statistic->utime_power_cons;
821 pc.duration = statistic->duration;
823 update_proc_power_cons(&pc, day);
825 eina_iterator_free(it);
832 int nlproc_stat_exit()
835 eina_hash_free(active_pids);
836 /* TODO: free terminated keys */