tizen 2.3 release
[kernel/api/system-resource.git] / src / cpu / logging-cpu.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 /*
20  * @file logging-cpu.c
21  *
22  * @desc start cpu logging system for resourced
23  *
24  * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
25  *
26  */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/vfs.h>
36 #include <linux/limits.h>
37
38 #include <ctype.h>
39 #include <stddef.h>
40
41 #include <dirent.h>
42 #include <sys/utsname.h>
43 #include <systemd/sd-journal.h>
44
45 #include "resourced.h"
46 #include "trace.h"
47 #include "module.h"
48 #include "macro.h"
49 #include "proc-process.h"
50 #include "logging.h"
51
52 #define PROC_STAT_PATH "/proc/%d/stat"
53 #define CPU_NAME "cpu"
54 #define CPU_COMMIT_INTERVAL             30*60   /* 20 min */
55
56 #define CPU_MAX_INTERVAL                20*60   /* 5 min */
57 #define CPU_INIT_INTERVAL               20*60   /* 3 min */
58 #define CPU_FOREGRD_INTERVAL            3*60    /* 1 min */
59 #define CPU_BACKGRD_INTERVAL            10*60   /* 2 min */
60 #define CPU_BACKGRD_OLD_INTERVAL        15*60   /* 5 min */
61
62 struct logging_cpu_info {
63         unsigned long utime;
64         unsigned long stime;
65         unsigned long last_utime;
66         unsigned long last_stime;
67         bool last_commited;
68         time_t last_log_time;
69         time_t log_interval;
70         pid_t last_pid;
71 };
72
73 static int get_cpu_time(pid_t pid, unsigned long *utime,
74         unsigned long *stime)
75 {
76         char proc_path[sizeof(PROC_STAT_PATH) + MAX_DEC_SIZE(int)];
77         FILE *fp;
78
79         assert(utime != NULL);
80         assert(stime != NULL);
81
82         sprintf(proc_path, PROC_STAT_PATH, pid);
83         fp = fopen(proc_path, "r");
84         if (fp == NULL)
85                 return RESOURCED_ERROR_FAIL;
86
87         if (fscanf(fp, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s") < 0) {
88                 fclose(fp);
89                 return RESOURCED_ERROR_FAIL;
90         }
91
92         if (fscanf(fp, "%lu %lu", utime, stime) < 1) {
93                 fclose(fp);
94                 return RESOURCED_ERROR_FAIL;
95         }
96
97         fclose(fp);
98
99         return RESOURCED_ERROR_NONE;
100 }
101
102 static void update_log_interval(struct logging_cpu_info *loginfo, int oom,
103         int always)
104 {
105         if (!always && (oom < OOMADJ_FOREGRD_LOCKED))
106                 return;
107
108         switch (oom) {
109         case OOMADJ_DISABLE:
110         case OOMADJ_SERVICE_MIN:
111         case OOMADJ_SU:
112                 loginfo->log_interval = CPU_MAX_INTERVAL;
113                 break;
114         case OOMADJ_INIT:
115                 loginfo->log_interval = CPU_INIT_INTERVAL;
116                 break;
117         case OOMADJ_FOREGRD_LOCKED:
118         case OOMADJ_FOREGRD_UNLOCKED:
119         case OOMADJ_BACKGRD_LOCKED:
120         case OOMADJ_SERVICE_DEFAULT:
121         case OOMADJ_SERVICE_FOREGRD:
122                 loginfo->log_interval = CPU_FOREGRD_INTERVAL;
123                 break;
124         case OOMADJ_BACKGRD_UNLOCKED:
125         case OOMADJ_SERVICE_BACKGRD:
126                 loginfo->log_interval = CPU_BACKGRD_INTERVAL;
127                 break;
128         default:
129                 if (oom > OOMADJ_BACKGRD_UNLOCKED)
130                         loginfo->log_interval = CPU_BACKGRD_OLD_INTERVAL;
131                 break;
132         }
133 }
134
135 static int init_cpu_info(void **pl, int pid, int oom, time_t now)
136 {
137         struct logging_cpu_info *info;
138
139         info = (struct logging_cpu_info *)
140                         malloc(sizeof(struct logging_cpu_info));
141         if (!info) {
142                 _E("malloc for logging_cpu_info is failed");
143                 return RESOURCED_ERROR_FAIL;
144         }
145         info->last_pid = pid;
146         info->utime = 0;
147         info->stime = 0;
148         info->last_utime = 0;
149         info->last_stime = 0;
150         info->last_log_time = now;
151         info->last_commited = false;
152
153         update_log_interval(info, oom, 1);
154         *pl = (void *)info;
155         return RESOURCED_ERROR_NONE;
156 }
157
158 /* pss_interval should be adjusted depending on app type */
159 static int update_cpu_info(void *pl, pid_t pid, int oom,
160         time_t now, unsigned always)
161 {
162         int ret = RESOURCED_ERROR_NONE;
163         struct logging_cpu_info *loginfo = (struct logging_cpu_info *)pl;
164         unsigned long utime = 0, stime = 0;
165         unsigned long utime_diff = 0, stime_diff = 0;
166
167         if (!always) {
168                 unsigned long update_interval = now - loginfo->last_log_time;
169                 if (update_interval < CPU_MAX_INTERVAL) {
170                         if (now < loginfo->last_log_time + loginfo->log_interval)
171                                 return ret;
172                 } else {
173                         loginfo->last_log_time = now;
174                 }
175         }
176
177         ret = get_cpu_time(pid, &utime, &stime);
178
179         if (ret != RESOURCED_ERROR_NONE)
180                 return ret;
181
182         if (loginfo->last_pid == pid) {
183                 if (loginfo->last_utime > utime)
184                         goto out;
185
186                 utime_diff = utime - loginfo->last_utime;
187                 loginfo->last_utime = utime;
188                 loginfo->utime += utime_diff;
189                 if (loginfo->stime > stime)
190                         goto out;
191                 stime_diff = stime - loginfo->last_stime;
192                 loginfo->last_stime = stime;
193                 loginfo->stime += stime_diff;
194
195         } else {
196                 loginfo->last_pid = pid;
197                 loginfo->utime += utime;
198                 loginfo->stime += stime;
199                 loginfo->last_utime = utime;
200                 loginfo->last_stime = stime;
201         }
202
203 out:
204         loginfo->last_log_time = now;
205         loginfo->last_commited = false;
206         update_log_interval(loginfo, oom, 0);
207         return ret;
208 }
209
210 static int write_cpu_info(char *name, struct logging_infos *infos,
211         int ss_index)
212 {
213         struct logging_cpu_info *ci = infos->stats[ss_index];
214
215         if (!infos->running && ci->last_commited)
216                 return RESOURCED_ERROR_NONE;
217
218         sd_journal_send("NAME=cpu",
219                 "TIME=%ld", ci->last_log_time,
220                 "PNAME=%s", name,
221                 "UTIME=%ld", ci->utime,
222                 "STIME=%ld", ci->stime,
223                 NULL);
224
225         ci->last_commited = true;
226
227         return RESOURCED_ERROR_NONE;
228 }
229
230 static struct logging_info_ops cpu_info_ops = {
231         .update = update_cpu_info,
232         .write  = write_cpu_info,
233         .init   = init_cpu_info,
234 };
235
236 static int logging_cpu_init(void *data)
237 {
238         int ret;
239
240         ret = register_logging_subsystem(CPU_NAME, &cpu_info_ops);
241         if(ret != RESOURCED_ERROR_NONE) {
242                 _E("register logging subsystem failed");
243                 return RESOURCED_ERROR_FAIL;
244         }
245         ret = update_commit_interval(CPU_NAME, CPU_COMMIT_INTERVAL);
246         if(ret != RESOURCED_ERROR_NONE) {
247                 _E("update commit interval logging subsystem failed");
248                 return RESOURCED_ERROR_FAIL;
249         }
250
251         _D("logging cpu init finished");
252         return RESOURCED_ERROR_NONE;
253 }
254
255 static int logging_cpu_exit(void *data)
256 {
257         _D("logging cpu exit");
258         return RESOURCED_ERROR_NONE;
259 }
260
261 static struct module_ops logging_cpu_ops = {
262         .priority       = MODULE_PRIORITY_NORMAL,
263         .name           = "logging_cpu",
264         .init           = logging_cpu_init,
265         .exit           = logging_cpu_exit,
266 };
267
268 MODULE_REGISTER(&logging_cpu_ops)