runtime-info: added process Memory and CPU usage APIs
[platform/core/api/runtime-info.git] / src / runtime_info_usage.c
1 /*
2  * Copyright (c) 2015 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 #include <stdio.h>
18 #include <string.h>
19 #include <unistd.h>
20
21 #include <dlog.h>
22
23 #include <runtime_info.h>
24 #include <runtime_info_private.h>
25
26 #include <E_DBus.h>
27
28 #ifdef LOG_TAG
29 #undef LOG_TAG
30 #endif
31 #define LOG_TAG "CAPI_RUNTIME_INFO_USAGE"
32
33 #define _E(fmt, arg...) LOGE("[%s,%d] "fmt, __FUNCTION__, __LINE__, ##arg)
34 #define _D(fmt, arg...) LOGD("[%s,%d] "fmt, __FUNCTION__, __LINE__, ##arg)
35 #define _I(fmt, arg...) LOGI("[%s,%d] "fmt, __FUNCTION__, __LINE__, ##arg)
36
37 #define RESOURCED_BUS_NAME "org.tizen.resourced"
38 #define RESOURCED_USAGE_OBJECT_NAME "/Org/Tizen/ResourceD/Process"
39 #define RESOURCED_USAGE_INTERFACE_NAME "org.tizen.resourced.process"
40 #define RESOURCED_MEMORY_USAGE_METHOD "ProcMemoryUsage"
41 #define RESOURCED_CPU_USAGE_METHOD "ProcCpuUsage"
42 #define DBUS_REPLY_TIMEOUT (120 * 1000)
43 #define MEMORY_USAGE 1
44 #define CPU_USAGE 2
45
46 #define kBtoKiB(val) (int)(((long long)val * 1024)/1000)
47
48 static int runtime_info_get_dbus_error(const char *err_name)
49 {
50         int size;
51
52         if (!err_name)
53                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
54
55         size = strlen(err_name);
56         if (!strncmp(err_name, DBUS_ERROR_IO_ERROR, size))
57                 return RUNTIME_INFO_ERROR_IO_ERROR;
58         else if (!strncmp(err_name, DBUS_ERROR_NO_MEMORY, size))
59                 return RUNTIME_INFO_ERROR_OUT_OF_MEMORY;
60         else if (!strncmp(err_name, DBUS_ERROR_ACCESS_DENIED, size))
61                 return RUNTIME_INFO_ERROR_PERMISSION_DENIED;
62         else
63                 return RUNTIME_INFO_ERROR_REMOTE_IO;
64 }
65
66 /* Handler function which handles dbus related instructions
67  * for both per process memory and cpu requests.
68  * Creates the method call to resourced and receives the reply (if successful)
69  * Return the received reply (if received) else NULL to signify failed call to resourced
70  */
71 static DBusMessage *runtime_info_dbus_process_usage_info(int *pid, int size, int info, int *error)
72 {
73         DBusConnection *conn;
74         DBusMessage *msg;
75         DBusMessage *reply;
76         DBusError err;
77         int ret;
78
79         if (!pid || !error) {
80                 _E("INVALID_PARAMETER(0x%08x): pid list and error params cannot be null");
81
82                 if (error)
83                         *error = RUNTIME_INFO_ERROR_INVALID_PARAMETER;
84                 return NULL;
85         }
86
87         conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
88         if (!conn) {
89                 _E("DBUS_CONNECTION_ERROR");
90                 *error = RUNTIME_INFO_ERROR_REMOTE_IO;
91                 return NULL;
92         }
93
94         if (info == MEMORY_USAGE) {
95                 _D("Process %d: received query to get process memory info of %d processes",
96                                 getpid(), size);
97                 msg = dbus_message_new_method_call(RESOURCED_BUS_NAME,
98                                 RESOURCED_USAGE_OBJECT_NAME,
99                                 RESOURCED_USAGE_INTERFACE_NAME,
100                                 RESOURCED_MEMORY_USAGE_METHOD);
101         } else if (info == CPU_USAGE) {
102                 _D("Process %d: received query to get process cpu usage of %d processes",
103                                 getpid(), size);
104                 msg = dbus_message_new_method_call(RESOURCED_BUS_NAME,
105                                 RESOURCED_USAGE_OBJECT_NAME,
106                                 RESOURCED_USAGE_INTERFACE_NAME,
107                                 RESOURCED_CPU_USAGE_METHOD);
108         } else {
109                 _E("INVALID_PARAMETER(0x%08x): info parameter should be %d or %d",
110                                 MEMORY_USAGE, CPU_USAGE, RUNTIME_INFO_ERROR_INVALID_PARAMETER);
111                 *error = RUNTIME_INFO_ERROR_INVALID_PARAMETER;
112                 return NULL;
113         }
114
115         if (!msg) {
116                 _E("DBUS_METHOD_CALL: not able to create method call (%s:%s-%s)",
117                                 RESOURCED_USAGE_OBJECT_NAME, RESOURCED_USAGE_INTERFACE_NAME,
118                                 RESOURCED_MEMORY_USAGE_METHOD);
119                 *error = RUNTIME_INFO_ERROR_OUT_OF_MEMORY;
120                 return NULL;
121         }
122
123         ret = dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &pid,
124                         size, DBUS_TYPE_INVALID);
125         if (!ret) {
126                 _E("DBUS_METHOD_CALL: not able to append pid array to message");
127                 *error = RUNTIME_INFO_ERROR_IO_ERROR;
128                 dbus_message_unref(msg);
129                 return NULL;
130         }
131
132         dbus_error_init(&err);
133         *error = 0;
134
135         if (info == MEMORY_USAGE)
136                 _D("Process %d: Sending dbus message to resourced for process memory usage info",
137                                 getpid());
138         else
139                 _D("Process %d: Sending dbus message to resourced for process cpu usage info",
140                                 getpid());
141         reply = dbus_connection_send_with_reply_and_block(conn, msg,
142                         DBUS_REPLY_TIMEOUT, &err);
143         if (!reply)
144                 _E("DBUS_METHOD_CALL: not able to send message");
145
146         if (dbus_error_is_set(&err)) {
147                 _E("DBUS_METHOD_CALL: dbus_connection_send error(%s:%s)", err.name, err.message);
148                 *error = runtime_info_get_dbus_error(err.name);
149                 dbus_error_free(&err);
150                 reply = NULL;
151         }
152
153         dbus_message_unref(msg);
154         return reply;
155 }
156
157 API int runtime_info_get_system_memory_info(runtime_memory_info_s *info)
158 {
159         FILE *meminfo_fp;
160         char buf[256];
161         unsigned long swap_total, swap_free, value;
162
163         if (info == NULL) {
164                 _E("INVALID_PARAMETER(0x%08x) : invalid output param",
165                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
166                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
167         }
168
169         swap_total = swap_free = 0;
170
171         meminfo_fp = fopen("/proc/meminfo", "r");
172         if (meminfo_fp == NULL) {
173                 _E("IO_ERROR(0x%08x) : failed to open file to read memory usage",
174                                 RUNTIME_INFO_ERROR_IO_ERROR);
175                 return RUNTIME_INFO_ERROR_IO_ERROR;
176         }
177
178         while (fgets(buf, sizeof(buf), meminfo_fp) != NULL) {
179                 if (sscanf(buf, "MemTotal: %lu", &value) == 1)
180                         info->total = kBtoKiB(value);
181                 else if (sscanf(buf, "MemFree: %lu", &value) == 1)
182                         info->free = kBtoKiB(value);
183                 else if (sscanf(buf, "Cached: %lu", &value) == 1)
184                         info->cache = kBtoKiB(value);
185                 else if (sscanf(buf, "SwapTotal: %lu", &value) == 1)
186                         swap_total = value;
187                 else if (sscanf(buf, "SwapFree: %lu", &value) == 1)
188                         swap_free = value;
189         }
190         fclose(meminfo_fp);
191
192         info->used = (info->total > info->free) ? (info->total - info->free) : 0;
193         info->swap = kBtoKiB(((swap_total > swap_free) ? (int)(swap_total - swap_free) : 0));
194
195         return RUNTIME_INFO_ERROR_NONE;
196 }
197
198 API int runtime_info_get_process_memory_info(int *pid, int size, process_memory_info_s **info)
199 {
200         DBusMessageIter iter, iter_array, iter_struct;
201         int index, value;
202         int error;
203         process_memory_info_s *proc_info;
204         DBusMessage *replymsg;
205
206         if (!pid || size <= 0) {
207                 _E("INVALID_PARAMETER(0x%08x) : invalid input param",
208                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
209                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
210         }
211
212         if (!info) {
213                 _E("INVALID_PARAMETER(0x%08x) : invalid output param",
214                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
215                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
216         }
217
218         *info = NULL;
219
220         /* Get the needed information from resourced daemon using dbus */
221         replymsg = runtime_info_dbus_process_usage_info(pid, size, MEMORY_USAGE, &error);
222         if (!replymsg) {
223                 _E("DBUS_METHOD_CALL: call to resourced not successful");
224                 return error;
225         }
226
227         /* Check if the message is an error message or not in expected
228          * format and return error value */
229         dbus_message_iter_init(replymsg, &iter);
230         if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INT32) {
231                 dbus_message_iter_get_basic(&iter, &error);
232                 _E("DBUS_METHOD_CALL: call to resourced returned error message");
233                 dbus_message_unref(replymsg);
234                 return error;
235         } else if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
236                 _E("DBUS_METHOD_CALL: received dbus message is not in expected format");
237                 dbus_message_unref(replymsg);
238                 return RUNTIME_INFO_ERROR_REMOTE_IO;
239         }
240
241         /* Populate the entries of info array using the data received from resourced */
242         *info = (process_memory_info_s *)malloc(size * sizeof(process_memory_info_s));
243         if (!info) {
244                 _E("OUT_OF_MEMORY(0x%08x)", RUNTIME_INFO_ERROR_OUT_OF_MEMORY);
245                 dbus_message_unref(replymsg);
246                 return RUNTIME_INFO_ERROR_OUT_OF_MEMORY;
247         }
248
249         dbus_message_iter_recurse(&iter, &iter_array);
250         for (index = 0; index < size; ++index) {
251                 proc_info = &(*info)[index];
252
253                 dbus_message_iter_recurse(&iter_array, &iter_struct);
254
255                 dbus_message_iter_get_basic(&iter_struct, &value);
256                 proc_info->vsz = value;
257                 dbus_message_iter_next(&iter_struct);
258                 dbus_message_iter_get_basic(&iter_struct, &value);
259                 proc_info->rss = value;
260                 dbus_message_iter_next(&iter_struct);
261                 dbus_message_iter_get_basic(&iter_struct, &value);
262                 proc_info->pss = value;
263                 dbus_message_iter_next(&iter_struct);
264                 dbus_message_iter_get_basic(&iter_struct, &value);
265                 proc_info->shared_clean = value;
266                 dbus_message_iter_next(&iter_struct);
267                 dbus_message_iter_get_basic(&iter_struct, &value);
268                 proc_info->shared_dirty = value;
269                 dbus_message_iter_next(&iter_struct);
270                 dbus_message_iter_get_basic(&iter_struct, &value);
271                 proc_info->private_clean = value;
272                 dbus_message_iter_next(&iter_struct);
273                 dbus_message_iter_get_basic(&iter_struct, &value);
274                 proc_info->private_dirty = value;
275
276                 _D("Index %d: Process %d, vsz %d, rss %d, pss %d, sh_clean %d, sh_dirty %d, pr_clean %d, pr_dirty %d",
277                                index, pid[index], proc_info->vsz, proc_info->rss, proc_info->pss,
278                                proc_info->shared_clean, proc_info->shared_dirty,
279                                proc_info->private_clean, proc_info->private_dirty);
280                 dbus_message_iter_next(&iter_array);
281         }
282
283         dbus_message_unref(replymsg);
284         return RUNTIME_INFO_ERROR_NONE;
285 }
286
287 API int runtime_info_get_cpu_usage(runtime_cpu_usage_s *usage)
288 {
289         FILE *cpuinfo_fp;
290         char buf[256];
291         unsigned long long user, nice, system, idle, iowait, irq, softirq, total_uptime;
292
293         if (usage == NULL) {
294                 _E("INVALID_PARAMETER(0x%08x) : invalid output param",
295                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
296                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
297         }
298
299         cpuinfo_fp = fopen("/proc/stat", "r");
300         if (cpuinfo_fp == NULL) {
301                 _E("IO_ERROR(0x%08x) : failed to open file to read cpu usage",
302                                 RUNTIME_INFO_ERROR_IO_ERROR);
303                 return RUNTIME_INFO_ERROR_IO_ERROR;
304         }
305
306         while (fgets(buf, sizeof(buf), cpuinfo_fp) != NULL) {
307                 if (!strncmp(buf, "cpu ", 4) &&
308                     sscanf(buf+4, "%llu %llu %llu %llu %llu %llu %llu",
309                                 &user, &nice, &system, &idle,
310                                 &iowait, &irq, &softirq) == 7)
311                         break;
312         }
313         fclose(cpuinfo_fp);
314
315         total_uptime = user+nice+system+idle+iowait+irq+softirq;
316
317         usage->user = (double)user*100/total_uptime;
318         usage->nice = (double)nice*100/total_uptime;
319         usage->system = (double)system*100/total_uptime;
320         usage->iowait = (double)iowait*100/total_uptime;
321
322         return RUNTIME_INFO_ERROR_NONE;
323 }
324
325 API int runtime_info_get_process_cpu_usage(int *pid, int size, process_cpu_usage_s **usage)
326 {
327         DBusMessageIter iter, iter_array, iter_struct;
328         int index, value;
329         int error;
330         process_cpu_usage_s *proc_usage;
331         DBusMessage *replymsg;
332
333         if (!pid || size <= 0) {
334                 _E("INVALID_PARAMETER(0x%08x) : invalid input param",
335                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
336                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
337         }
338
339         if (!usage) {
340                 _E("INVALID_PARAMETER(0x%08x) : invalid output param",
341                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
342                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
343         }
344
345         *usage = NULL;
346
347         /* Get the needed information from resourced daemon using dbus */
348         replymsg = runtime_info_dbus_process_usage_info(pid, size, CPU_USAGE, &error);
349         if (!replymsg) {
350                 _E("DBUS_METHOD_CALL: call to resourced not successful");
351                 return error;
352         }
353
354         /* Check if the message is an error message or not in expected format
355          * and return error value */
356         dbus_message_iter_init(replymsg, &iter);
357         if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INT32) {
358                 dbus_message_iter_get_basic(&iter, &error);
359                 _E("DBUS_METHOD_CALL: call to resourced returned error message");
360                 dbus_message_unref(replymsg);
361                 return error;
362         } else if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
363                 _E("DBUS_METHOD_CALL: received dbus message is not in expected format");
364                 dbus_message_unref(replymsg);
365                 return RUNTIME_INFO_ERROR_REMOTE_IO;
366         }
367
368         /* Populate the entries of info array using the data received from resourced */
369         *usage = (process_cpu_usage_s *)malloc(size * sizeof(process_cpu_usage_s));
370         if (!(*usage)) {
371                 _E("OUT_OF_MEMORY(0x%08x)", RUNTIME_INFO_ERROR_OUT_OF_MEMORY);
372                 dbus_message_unref(replymsg);
373                 return RUNTIME_INFO_ERROR_OUT_OF_MEMORY;
374         }
375
376         dbus_message_iter_recurse(&iter, &iter_array);
377         for (index = 0; index < size; ++index) {
378                 proc_usage = &(*usage)[index];
379
380                 dbus_message_iter_recurse(&iter_array, &iter_struct);
381
382                 dbus_message_iter_get_basic(&iter_struct, &value);
383                 proc_usage->utime = value;
384                 dbus_message_iter_next(&iter_struct);
385                 dbus_message_iter_get_basic(&iter_struct, &value);
386                 proc_usage->stime = value;
387
388                 _D("Index %d: Process %d, utime %d, stime %d", index, pid[index],
389                                 proc_usage->utime, proc_usage->stime);
390                 dbus_message_iter_next(&iter_array);
391         }
392
393         dbus_message_unref(replymsg);
394         return RUNTIME_INFO_ERROR_NONE;
395 }