cpu : check whether malloc is failed
[platform/core/system/batterymonitor-plugins.git] / plugin / cpu / src / bm_cpu_plugin.c
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <glib.h>
21 #include <gio/gio.h>
22 #include <sqlite3.h>
23 #include <sys/time.h>
24 #include <sys/types.h>
25
26 #include <app_manager.h>
27 #include <runtime_info.h>
28
29 #include "bm_plugin_interface.h"
30 #include "bm_cpu_util.h"
31 #include "bm_log.h"
32
33 enum heart_cpu_status {
34         SERVICE = 0,
35         FOREG = 1,
36         BACKG = 2,
37 };
38
39 struct app_status {
40         pid_t pid;
41         time_t last_checkpoint; // Need to get usage from this time
42         time_t used_time;       // utime + stime
43         enum heart_cpu_status last_status;
44 };
45
46 static struct timeval last_requested_time;
47
48 static GDBusConnection *dbus_connection;
49 static GHashTable *running_app_list;    // Key(appid), Val(app_status)
50
51 gint find_app_time(gconstpointer a, gconstpointer b)
52 {
53         const char *appid = (const char *)b;
54         const app_time_map_st1 *app_time = (const app_time_map_st1 *)a;
55
56         return g_strcmp0(appid, app_time->app_id);
57 }
58
59 void free_atm_st1(gpointer data)
60 {
61         app_time_map_st1 *atm = (app_time_map_st1 *)data;
62
63         if (!atm)
64                 return;
65
66         if (atm->app_id)
67                 free(atm->app_id);
68         free(atm);
69 }
70
71 bool is_running_app(const char *appid)
72 {
73         bool is_running = false;
74         int ret = app_manager_is_running(appid, &is_running);
75         if (ret != APP_MANAGER_ERROR_NONE) {
76                 _E("app_manager_is_running failed (%d)", ret);
77                 return false;
78         }
79
80         return is_running;
81 }
82
83 int deinit()
84 {
85         ENTER;
86
87         if (dbus_connection) {
88                 g_object_unref(dbus_connection);
89                 dbus_connection = NULL;
90         }
91
92         if (running_app_list) {
93                 g_hash_table_destroy(running_app_list);
94                 running_app_list = NULL;
95         }
96
97         EXIT;
98         return BM_PLUGIN_ERROR_NONE;
99 }
100
101 int init()
102 {
103         ENTER;
104
105         GError *g_err = NULL;
106
107         if (gettimeofday(&last_requested_time, NULL) != 0) {
108                 _E("gettimeofday failed : %m");
109                 goto failed;
110         }
111
112         dbus_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &g_err);
113         if (!dbus_connection) {
114                 _E("g_bus_get_sync failed : %s", g_err->message ? g_err->message : NULL);
115                 g_error_free(g_err);
116                 goto failed;
117         }
118
119         running_app_list = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
120         if (!running_app_list) {
121                 _E("g_hash_table_new_full failed");
122                 goto failed;
123         }
124
125         EXIT;
126         return BM_PLUGIN_ERROR_NONE;
127
128 failed:
129         deinit();
130
131         EXIT;
132         return BM_PLUGIN_ERROR_OUT_OF_MEMORY;
133 }
134
135 int get_feature_data(bm_data_h *handle, bm_plugin_data_type_e type)
136 {
137         ENTER;
138
139         int ret;
140
141         bm_cpu_st *usage = NULL;
142
143         struct timeval tv;
144
145         GError *g_err = NULL;
146         GDBusMessage *msg_req, *msg_reply;
147
148         /* Sqlite */
149         sqlite3 *db;
150         sqlite3_stmt *stmt;
151         char query[128];
152
153         /* DB row */
154         unsigned int time;
155         const char *appid;
156         const char *data;
157         unsigned int utime, stime, pid, status;
158
159         /* App status */
160         struct app_status *app_status, *new_app_status;
161         int elapsed;
162         process_cpu_usage_s *cpu_usage;
163         GHashTableIter iter;
164         gpointer g_key, g_val;
165
166         /* App time map */
167         app_time_map_st1 *app_time_map;
168
169
170         /* Check argument */
171         if (!handle || type != BM_DATA_TYPE_CPU) {
172                 _E("Invalid argument");
173                 return BM_PLUGIN_ERROR_INVALID_PARAMETER;
174         }
175
176         /* Get current time */
177         ret = gettimeofday(&tv, NULL);
178         if (ret != 0) {
179                 _E("gettimeofday failed : %m");
180
181                 EXIT;
182                 return BM_PLUGIN_ERROR_OUT_OF_MEMORY;
183         }
184
185         /* Request for resourced to update HEART-CPU DB */
186         msg_req = g_dbus_message_new_method_call(
187                         "org.tizen.resourced",
188                         "/Org/Tizen/ResourceD/Logging",
189                         "org.tizen.resourced.logging",
190                         "SyncCpuData"
191                         );
192         if (!msg_req) {
193                 if (g_err) {
194                         _E("g_dbus_message_new_method_call failed : %s", g_err->message ? g_err->message : NULL);
195                         g_error_free(g_err);
196                 } else
197                         _E("g_dbus_message_new_method_call failed");
198
199                 EXIT;
200                 return BM_PLUGIN_ERROR_NOT_SUPPORTED;
201         }
202
203         msg_reply = g_dbus_connection_send_message_with_reply_sync(dbus_connection, msg_req, G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, NULL, NULL, &g_err);
204         if (msg_reply)
205                 g_object_unref(msg_reply);
206         else {
207                 if (g_err) {
208                         _W("g_dbus_connection_send_message_with_reply_sync failed : %s", g_err->message ? g_err->message : NULL);
209                         g_error_free(g_err);
210                 } else
211                         _W("g_dbus_connection_send_message_with_reply_sync failed");
212         }
213
214         /* Open HEART-CPU DB */
215         ret = sqlite3_open("/opt/usr/home/owner/.applications/dbspace/.resourced-heart-default.db", &db);
216         if (ret != SQLITE_OK) {
217                 _E("sqlite3_open failed (%d)", ret);
218
219                 EXIT;
220                 return BM_PLUGIN_ERROR_NO_DATA;
221         }
222
223         /* Make usage data structure */
224         usage = (bm_cpu_st *)calloc(1, sizeof(bm_cpu_st));
225         if (!usage) {
226                 _E("calloc failed");
227
228                 EXIT;
229                 return BM_PLUGIN_ERROR_OUT_OF_MEMORY;
230         }
231
232         /* Read data from HEART-CPU DB */
233         _I("Gather CPU usage : %ld ~ %ld", last_requested_time.tv_sec, tv.tv_sec);
234         snprintf(query, 128, "SELECT time,appid,data FROM cpu WHERE time >= %ld", last_requested_time.tv_sec);
235         ret = sqlite3_prepare_v2(db, query, -1, &stmt, 0);
236         while (sqlite3_step(stmt) == SQLITE_ROW) {
237                 time = sqlite3_column_int(stmt, 0);
238                 appid = (const char *)sqlite3_column_text(stmt, 1);
239                 data = (const char *)sqlite3_column_text(stmt, 2);
240                 sscanf(data, "%u %u %u %u", &utime, &stime, &pid, &status);
241
242                 //_D("time(%u), appid(%s), utime(%u), stime(%u), pid(%u), status(%u)", time, appid, utime, stime, pid, status);
243
244                 /* Ignore terminated app */
245                 if (!is_running_app(appid)) {
246                         _D("%s is already terminated", appid);
247                         g_hash_table_remove(running_app_list, appid);
248                         continue;
249                 }
250
251                 app_status = g_hash_table_lookup(running_app_list, appid);
252
253                 /* Insert newly launched app in the running app list */
254                 if (!app_status) {
255                         _D("%s is newly launched", appid);
256                         new_app_status = malloc(sizeof(struct app_status));
257                         if (!new_app_status) {
258                                 _E("malloc failed");
259                                 ret = BM_PLUGIN_ERROR_OUT_OF_MEMORY;
260                                 goto clean_atm;
261                         }
262                         new_app_status->pid = pid;
263                         new_app_status->last_checkpoint = time;
264                         new_app_status->used_time = utime + stime;
265                         new_app_status->last_status = status;
266                         g_hash_table_insert(running_app_list, g_strdup(appid), new_app_status);
267                         continue;
268                 }
269
270                 /* Initialize restarted app */
271                 if (app_status->pid != pid) {
272                         _D("%s is restarted", appid);
273                         app_status->pid = pid;
274                         app_status->last_checkpoint = time;
275                         app_status->used_time = utime + stime;
276                         app_status->last_status = status;
277                         continue;
278                 }
279
280                 /* Ignore CPU unused app */
281                 elapsed = utime + stime - app_status->used_time;
282                 if (elapsed <= 0) {
283                         _D("%s doesn't use CPU. Ignore it", appid);
284                         continue;
285                 }
286
287                 /* Accumulate used time */
288                 _D("%s uses CPU (+%d)", appid, elapsed);
289                 usage->cpuTicks += elapsed;
290                 switch (app_status->last_status) {
291                 case FOREG:
292                         usage->cpuTimeForeground += elapsed;
293                         break;
294                 case BACKG:
295                 case SERVICE:
296                         usage->cpuTimeBackground += elapsed;
297                         break;
298                 }
299
300                 /* Insert the app using CPU in the ATM list */
301                 if (!g_slist_find_custom(usage->atm_list, appid, find_app_time)) {
302                         app_time_map = malloc(sizeof(app_time_map_st1));
303                         if (!app_time_map) {
304                                 _E("malloc failed");
305                                 ret = BM_PLUGIN_ERROR_OUT_OF_MEMORY;
306                                 goto clean_atm;
307                         }
308                         app_time_map->app_id = g_strdup(appid);
309                         if (!app_time_map->app_id) {
310                                 _E("g_strdup failed");
311                                 ret = BM_PLUGIN_ERROR_OUT_OF_MEMORY;
312                                 goto clean_atm;
313                         }
314                         app_time_map->time = tv.tv_sec - app_status->last_checkpoint;
315                         if (app_time_map->time <= 0)
316                                 app_time_map->time = 1;
317                         usage->atm_list = g_slist_append(usage->atm_list, app_time_map);
318                 }
319
320                 /* Update app status */
321                 app_status->used_time = utime + stime;
322                 app_status->last_status = status;
323         }
324
325         /* Update all running apps with current value */
326         g_hash_table_iter_init(&iter, running_app_list);
327         while (g_hash_table_iter_next(&iter, &g_key, &g_val)) {
328                 appid = (const char *)g_key;
329                 app_status = (struct app_status *)g_val;
330
331                 /* Ignore terminated app */
332                 if (!is_running_app(appid)) {
333                         _D("%s is already terminated", appid);
334                         g_hash_table_iter_remove(&iter);
335                         continue;
336                 }
337
338                 /* Get latest CPU time */
339                 ret = runtime_info_get_process_cpu_usage(&app_status->pid, 1, &cpu_usage);
340                 if (ret != RUNTIME_INFO_ERROR_NONE) {
341                         _E("runtime_info_get_process_cpu_usage for %u failed(%d)", app_status->pid, ret);
342                         continue;
343                 }
344
345                 app_status->last_checkpoint = tv.tv_sec;
346
347                 /* Ignore CPU unused app */
348                 elapsed = cpu_usage->utime + cpu_usage->stime - app_status->used_time;
349                 if (elapsed <= 0) {
350                         _D("%s doesn't use CPU. Ignore it", appid);
351                         continue;
352                 }
353
354                 /* Update CPU time */
355                 _D("%s uses CPU (+%d)", appid, elapsed);
356                 usage->cpuTicks += elapsed;
357                 switch (app_status->last_status) {
358                 case FOREG:
359                         usage->cpuTimeForeground += elapsed;
360                         break;
361                 case BACKG:
362                 case SERVICE:
363                         usage->cpuTimeBackground += elapsed;
364                         break;
365                 }
366
367                 /* Insert the app using CPU in the ATM list */
368                 if (!g_slist_find_custom(usage->atm_list, appid, find_app_time)) {
369                         app_time_map = malloc(sizeof(app_time_map_st1));
370                         if (!app_time_map) {
371                                 _E("malloc failed");
372                                 ret = BM_PLUGIN_ERROR_OUT_OF_MEMORY;
373                                 goto clean_atm;
374                         }
375                         app_time_map->app_id = g_strdup(appid);
376                         if (!app_time_map->app_id) {
377                                 _E("g_strdup failed");
378                                 ret = BM_PLUGIN_ERROR_OUT_OF_MEMORY;
379                                 goto clean_atm;
380                         }
381                         app_time_map->time = tv.tv_sec - last_requested_time.tv_sec;
382                         if (app_time_map->time <= 0)
383                                 app_time_map->time = 1;
384                         usage->atm_list = g_slist_append(usage->atm_list, app_time_map);
385                 }
386
387                 app_status->used_time = cpu_usage->utime + cpu_usage->stime;
388
389                 free(cpu_usage);
390         }
391
392         /* Update last requested time */
393         last_requested_time.tv_sec = tv.tv_sec;
394         last_requested_time.tv_usec = tv.tv_usec;
395
396         /* Return result */
397         *handle = usage;
398
399         _I("Succeed to get CPU usage");
400         ret = BM_PLUGIN_ERROR_NONE;
401         goto clean_db;
402
403 clean_atm:
404         g_slist_free_full(usage->atm_list, free_atm_st1);
405
406 clean_db:
407         sqlite3_finalize(stmt);
408         sqlite3_close(db);
409
410         EXIT;
411         return ret;
412 }
413
414 static bm_api_st cpu_api = {
415         .init = init,
416         .deinit = deinit,
417         .get_feature_data = get_feature_data
418 };
419
420 int plugin_init(bm_api_st **api)
421 {
422         ENTER;
423
424         *api = &cpu_api;
425
426         EXIT;
427         return 0;
428 }
429
430 int plugin_deinit(void)
431 {
432         ENTER;
433
434         EXIT;
435         return 0;
436 }
437
438 BM_PLUGIN_ADD(BM_PLUGIN_ID_CPU, NAME, AUTHOR, VERSION, plugin_init, plugin_deinit);