4a05400061fc9d1a9429abf08d5b5b9df248c222
[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 #define RESOURCED_BUS_NAME "org.tizen.resourced"
29 #define RESOURCED_USAGE_OBJECT_NAME "/Org/Tizen/ResourceD/Process"
30 #define RESOURCED_USAGE_INTERFACE_NAME "org.tizen.resourced.process"
31 #define RESOURCED_MEMORY_USAGE_METHOD "ProcMemoryUsage"
32 #define RESOURCED_CPU_USAGE_METHOD "ProcCpuUsage"
33 #define DBUS_REPLY_TIMEOUT (120 * 1000)
34 #define MEMORY_USAGE 1
35 #define CPU_USAGE 2
36
37 #define kBtoKiB(val) (int)(((long long)val * 1024)/1000)
38 #define pagetoKiB(val) (int)((long long)val * 4096 / 1000)
39
40 //LCOV_EXCL_START : system error
41 static int runtime_info_get_dbus_error(const char *err_name)
42 {
43         int size;
44
45         if (!err_name)
46                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
47
48         size = strlen(err_name);
49         if (!strncmp(err_name, DBUS_ERROR_IO_ERROR, size))
50                 return RUNTIME_INFO_ERROR_IO_ERROR;
51         else if (!strncmp(err_name, DBUS_ERROR_NO_MEMORY, size))
52                 return RUNTIME_INFO_ERROR_OUT_OF_MEMORY;
53         else if (!strncmp(err_name, DBUS_ERROR_ACCESS_DENIED, size))
54                 return RUNTIME_INFO_ERROR_PERMISSION_DENIED;
55         else
56                 return RUNTIME_INFO_ERROR_REMOTE_IO;
57 }
58 //LCOV_EXCL_STOP
59
60 /* Handler function which handles dbus related instructions
61  * for both per process memory and cpu requests.
62  * Creates the method call to resourced and receives the reply (if successful)
63  * Return the received reply (if received) else NULL to signify failed call to resourced
64  */
65 static DBusMessage *runtime_info_dbus_process_usage_info(int *pid, int size, int info, int *error)
66 {
67         DBusConnection *conn;
68         DBusMessage *msg;
69         DBusMessage *reply;
70         DBusError err;
71         int ret;
72
73         if (!pid || !error) {
74                 //LCOV_EXCL_START : system error
75                 _E("INVALID_PARAMETER(0x%08x): pid list and error params cannot be null");
76
77                 if (error)
78                         *error = RUNTIME_INFO_ERROR_INVALID_PARAMETER;
79                 return NULL;
80                 //LCOV_EXCL_STOP
81         }
82
83         conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
84         if (!conn) {
85                 //LCOV_EXCL_START : system error
86                 _E("DBUS_CONNECTION_ERROR");
87                 *error = RUNTIME_INFO_ERROR_REMOTE_IO;
88                 return NULL;
89                 //LCOV_EXCL_STOP
90         }
91
92         if (info == MEMORY_USAGE) {
93                 _D("Process %d: received query to get process memory info of %d processes",
94                                 getpid(), size);
95                 msg = dbus_message_new_method_call(RESOURCED_BUS_NAME,
96                                 RESOURCED_USAGE_OBJECT_NAME,
97                                 RESOURCED_USAGE_INTERFACE_NAME,
98                                 RESOURCED_MEMORY_USAGE_METHOD);
99         } else if (info == CPU_USAGE) {
100                 _D("Process %d: received query to get process cpu usage of %d processes",
101                                 getpid(), size);
102                 msg = dbus_message_new_method_call(RESOURCED_BUS_NAME,
103                                 RESOURCED_USAGE_OBJECT_NAME,
104                                 RESOURCED_USAGE_INTERFACE_NAME,
105                                 RESOURCED_CPU_USAGE_METHOD);
106         } else {
107                 _E("INVALID_PARAMETER(0x%08x): info parameter should be %d or %d",
108                                 MEMORY_USAGE, CPU_USAGE, RUNTIME_INFO_ERROR_INVALID_PARAMETER);
109                 *error = RUNTIME_INFO_ERROR_INVALID_PARAMETER;
110                 return NULL;
111         }
112
113         if (!msg) {
114                 _E("DBUS_METHOD_CALL: not able to create method call (%s:%s-%s)",
115                                 RESOURCED_USAGE_OBJECT_NAME, RESOURCED_USAGE_INTERFACE_NAME,
116                                 RESOURCED_MEMORY_USAGE_METHOD);
117                 *error = RUNTIME_INFO_ERROR_OUT_OF_MEMORY;
118                 return NULL;
119         }
120
121         ret = dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &pid,
122                         size, DBUS_TYPE_INVALID);
123         if (!ret) {
124                 //LCOV_EXCL_START : system error
125                 _E("DBUS_METHOD_CALL: not able to append pid array to message");
126                 *error = RUNTIME_INFO_ERROR_IO_ERROR;
127                 dbus_message_unref(msg);
128                 return NULL;
129                 //LCOV_EXCL_STOP
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");       //LCOV_EXCL_LINE : system error
145
146         if (dbus_error_is_set(&err)) {
147                 //LCOV_EXCL_START : system error
148                 _E("DBUS_METHOD_CALL: dbus_connection_send error(%s:%s)", err.name, err.message);
149                 *error = runtime_info_get_dbus_error(err.name);
150                 dbus_error_free(&err);
151                 reply = NULL;
152                 //LCOV_EXCL_STOP
153         }
154
155         dbus_message_unref(msg);
156         return reply;
157 }
158
159 API int runtime_info_get_system_memory_info(runtime_memory_info_s *info)
160 {
161         FILE *fp;
162         char buf[256];
163         unsigned long swap_total, swap_free, value, mem_available;
164
165         if (info == NULL) {
166                 _E("INVALID_PARAMETER(0x%08x) : invalid output param",
167                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
168                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
169         }
170
171         swap_total = swap_free = mem_available = 0;
172
173         fp = fopen("/proc/meminfo", "r");
174         if (fp == NULL) {
175                 _E("IO_ERROR(0x%08x) : failed to open file to read memory usage",
176                                 RUNTIME_INFO_ERROR_IO_ERROR);
177                 return RUNTIME_INFO_ERROR_IO_ERROR;
178         }
179
180         while (fgets(buf, sizeof(buf), fp) != NULL) {
181                 if (sscanf(buf, "MemTotal: %lu", &value) == 1)
182                         info->total = kBtoKiB(value);
183                 else if (sscanf(buf, "MemFree: %lu", &value) == 1)
184                         info->free = kBtoKiB(value);
185                 else if (sscanf(buf, "Cached: %lu", &value) == 1)
186                         info->cache = kBtoKiB(value);
187                 else if (sscanf(buf, "MemAvailable: %lu", &value) == 1)
188                         mem_available = kBtoKiB(value);
189                 else if (sscanf(buf, "SwapTotal: %lu", &value) == 1)
190                         swap_total = value;
191                 else if (sscanf(buf, "SwapFree: %lu", &value) == 1)
192                         swap_free = value;
193         }
194         fclose(fp);
195
196         info->used = mem_available ? (info->total - mem_available) : (info->total - info->free - info->cache);
197         info->swap = kBtoKiB(((swap_total > swap_free) ? (int)(swap_total - swap_free) : 0));
198
199         /*
200          * Get total memory size from zoneinfo
201          *
202          * meminfo show the total memory size
203          * except kernel reserved memory
204          *
205          * Therefore, we will use (the number of page * page size)
206          */
207         fp = fopen("/proc/zoneinfo", "r");
208         if (fp == NULL) {
209                 _E("IO_ERROR(0x%08x) : failed to open file to read memory size",
210                                 RUNTIME_INFO_ERROR_IO_ERROR);
211                 return RUNTIME_INFO_ERROR_IO_ERROR;
212         }
213
214         info->total = 0;
215         while (fgets(buf, sizeof(buf), fp) != NULL)
216                 if (sscanf(buf, " spanned %lu", &value) == 1)
217                         info->total += value;
218         info->total = pagetoKiB(info->total);
219
220         fclose(fp);
221
222         return RUNTIME_INFO_ERROR_NONE;
223 }
224
225 API int runtime_info_get_process_memory_info(int *pid, int size, process_memory_info_s **info)
226 {
227         DBusMessageIter iter, iter_array, iter_struct;
228         int index, value;
229         int error;
230         process_memory_info_s *proc_info;
231         DBusMessage *replymsg;
232
233         if (!pid || size <= 0) {
234                 _E("INVALID_PARAMETER(0x%08x) : invalid input param",
235                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
236                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
237         }
238
239         if (!info) {
240                 _E("INVALID_PARAMETER(0x%08x) : invalid output param",
241                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
242                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
243         }
244
245         *info = NULL;
246
247         /* Get the needed information from resourced daemon using dbus */
248         replymsg = runtime_info_dbus_process_usage_info(pid, size, MEMORY_USAGE, &error);
249         if (!replymsg) {
250                 //LCOV_EXCL_START : system error
251                 _E("DBUS_METHOD_CALL: call to resourced not successful");
252                 return error;
253                 //LCOV_EXCL_STOP
254         }
255
256         /* Check if the message is an error message or not in expected
257          * format and return error value */
258         dbus_message_iter_init(replymsg, &iter);
259         if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INT32) {
260                 //LCOV_EXCL_START : system error
261                 dbus_message_iter_get_basic(&iter, &error);
262                 _E("DBUS_METHOD_CALL: call to resourced returned error message");
263                 dbus_message_unref(replymsg);
264                 return error;
265                 //LCOV_EXCL_STOP
266         } else if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
267                 //LCOV_EXCL_START : system error
268                 _E("DBUS_METHOD_CALL: received dbus message is not in expected format");
269                 dbus_message_unref(replymsg);
270                 return RUNTIME_INFO_ERROR_REMOTE_IO;
271                 //LCOV_EXCL_STOP
272         }
273
274         /* Populate the entries of info array using the data received from resourced */
275         *info = (process_memory_info_s *)malloc(size * sizeof(process_memory_info_s));
276         if (!info) {
277                 _E("OUT_OF_MEMORY(0x%08x)", RUNTIME_INFO_ERROR_OUT_OF_MEMORY);
278                 dbus_message_unref(replymsg);
279                 return RUNTIME_INFO_ERROR_OUT_OF_MEMORY;
280         }
281
282         dbus_message_iter_recurse(&iter, &iter_array);
283         for (index = 0; index < size; ++index) {
284                 proc_info = &(*info)[index];
285
286                 dbus_message_iter_recurse(&iter_array, &iter_struct);
287
288                 dbus_message_iter_get_basic(&iter_struct, &value);
289                 proc_info->vsz = value;
290                 dbus_message_iter_next(&iter_struct);
291                 dbus_message_iter_get_basic(&iter_struct, &value);
292                 proc_info->rss = value;
293                 dbus_message_iter_next(&iter_struct);
294                 dbus_message_iter_get_basic(&iter_struct, &value);
295                 proc_info->pss = value;
296                 dbus_message_iter_next(&iter_struct);
297                 dbus_message_iter_get_basic(&iter_struct, &value);
298                 proc_info->shared_clean = value;
299                 dbus_message_iter_next(&iter_struct);
300                 dbus_message_iter_get_basic(&iter_struct, &value);
301                 proc_info->shared_dirty = value;
302                 dbus_message_iter_next(&iter_struct);
303                 dbus_message_iter_get_basic(&iter_struct, &value);
304                 proc_info->private_clean = value;
305                 dbus_message_iter_next(&iter_struct);
306                 dbus_message_iter_get_basic(&iter_struct, &value);
307                 proc_info->private_dirty = value;
308
309                 dbus_message_iter_next(&iter_array);
310         }
311
312         dbus_message_unref(replymsg);
313         return RUNTIME_INFO_ERROR_NONE;
314 }
315
316 API int runtime_info_get_cpu_usage(runtime_cpu_usage_s *usage)
317 {
318         FILE *cpuinfo_fp;
319         char buf[256];
320         unsigned long long user, nice, system, idle, iowait, irq, softirq, total_uptime;
321
322         if (usage == NULL) {
323                 _E("INVALID_PARAMETER(0x%08x) : invalid output param",
324                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
325                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
326         }
327
328         cpuinfo_fp = fopen("/proc/stat", "r");
329         if (cpuinfo_fp == NULL) {
330                 _E("IO_ERROR(0x%08x) : failed to open file to read cpu usage",
331                                 RUNTIME_INFO_ERROR_IO_ERROR);
332                 return RUNTIME_INFO_ERROR_IO_ERROR;
333         }
334
335         while (fgets(buf, sizeof(buf), cpuinfo_fp) != NULL) {
336                 if (!strncmp(buf, "cpu ", 4) &&
337                     sscanf(buf+4, "%llu %llu %llu %llu %llu %llu %llu",
338                                 &user, &nice, &system, &idle,
339                                 &iowait, &irq, &softirq) == 7)
340                         break;
341         }
342         fclose(cpuinfo_fp);
343
344         total_uptime = user+nice+system+idle+iowait+irq+softirq;
345
346         usage->user = (double)user*100/total_uptime;
347         usage->nice = (double)nice*100/total_uptime;
348         usage->system = (double)system*100/total_uptime;
349         usage->iowait = (double)iowait*100/total_uptime;
350
351         return RUNTIME_INFO_ERROR_NONE;
352 }
353
354 API int runtime_info_get_process_cpu_usage(int *pid, int size, process_cpu_usage_s **usage)
355 {
356         DBusMessageIter iter, iter_array, iter_struct;
357         int index, value;
358         int error;
359         process_cpu_usage_s *proc_usage;
360         DBusMessage *replymsg;
361
362         if (!pid || size <= 0) {
363                 _E("INVALID_PARAMETER(0x%08x) : invalid input param",
364                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
365                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
366         }
367
368         if (!usage) {
369                 _E("INVALID_PARAMETER(0x%08x) : invalid output param",
370                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
371                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
372         }
373
374         *usage = NULL;
375
376         /* Get the needed information from resourced daemon using dbus */
377         replymsg = runtime_info_dbus_process_usage_info(pid, size, CPU_USAGE, &error);
378
379         if (!replymsg) {
380                 //LCOV_EXCL_START : system error
381                 _E("DBUS_METHOD_CALL: call to resourced not successful");
382                 return error;
383                 //LCOV_EXCL_STOP
384         }
385
386         /* Check if the message is an error message or not in expected format
387          * and return error value */
388         dbus_message_iter_init(replymsg, &iter);
389         if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INT32) {
390                 //LCOV_EXCL_START : system error
391                 dbus_message_iter_get_basic(&iter, &error);
392                 _E("DBUS_METHOD_CALL: call to resourced returned error message");
393                 dbus_message_unref(replymsg);
394                 return error;
395                 //LCOV_EXCL_STOP
396         } else if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
397                 //LCOV_EXCL_START : system error
398                 _E("DBUS_METHOD_CALL: received dbus message is not in expected format");
399                 dbus_message_unref(replymsg);
400                 return RUNTIME_INFO_ERROR_REMOTE_IO;
401                 //LCOV_EXCL_STOP
402         }
403
404         /* Populate the entries of info array using the data received from resourced */
405         *usage = (process_cpu_usage_s *)malloc(size * sizeof(process_cpu_usage_s));
406         if (!(*usage)) {
407                 //LCOV_EXCL_START : system error
408                 _E("OUT_OF_MEMORY(0x%08x)", RUNTIME_INFO_ERROR_OUT_OF_MEMORY);
409                 dbus_message_unref(replymsg);
410                 return RUNTIME_INFO_ERROR_OUT_OF_MEMORY;
411                 //LCOV_EXCL_STOP
412         }
413
414         dbus_message_iter_recurse(&iter, &iter_array);
415         for (index = 0; index < size; ++index) {
416                 proc_usage = &(*usage)[index];
417
418                 dbus_message_iter_recurse(&iter_array, &iter_struct);
419
420                 dbus_message_iter_get_basic(&iter_struct, &value);
421                 proc_usage->utime = value;
422                 dbus_message_iter_next(&iter_struct);
423                 dbus_message_iter_get_basic(&iter_struct, &value);
424                 proc_usage->stime = value;
425
426                 dbus_message_iter_next(&iter_array);
427         }
428
429         dbus_message_unref(replymsg);
430         return RUNTIME_INFO_ERROR_NONE;
431 }
432
433 API int runtime_info_get_processor_count(int *num_core)
434 {
435         FILE *cpuinfo_fp;
436         int buf;
437         int result;
438
439         if (!num_core) {
440                 _E("INVALID_PARAMETER(0x%08x) : invalid output parameter",
441                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
442                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
443         }
444
445         cpuinfo_fp = fopen("/sys/devices/system/cpu/possible", "r");
446         if (cpuinfo_fp == NULL) {
447                 //LCOV_EXCL_START : system error
448                 _E("IO_ERROR(0x%08x) : failed to open file to read cpu information",
449                                 RUNTIME_INFO_ERROR_IO_ERROR);
450                 return RUNTIME_INFO_ERROR_IO_ERROR;
451                 //LCOV_EXCL_STOP
452         }
453
454         if (!fscanf(cpuinfo_fp, "%d-%d", &buf, &result)) {
455                 //LCOV_EXCL_START : system error
456                 _E("IO_ERROR(0x%08x) : there is no information in the system file",
457                                 RUNTIME_INFO_ERROR_IO_ERROR);
458                 fclose(cpuinfo_fp);
459                 return RUNTIME_INFO_ERROR_IO_ERROR;
460                 //LCOV_EXCL_STOP
461         }
462
463         *num_core = result + 1;
464
465         fclose(cpuinfo_fp);
466         return RUNTIME_INFO_ERROR_NONE;
467 }
468
469 API int runtime_info_get_processor_current_frequency(int core_idx, int *cpu_freq)
470 {
471         int num_core;
472
473         if (runtime_info_get_processor_count(&num_core)
474                         != RUNTIME_INFO_ERROR_NONE) {
475                 _E("runtime_info_get_processor_count is failed");
476                 return RUNTIME_INFO_ERROR_IO_ERROR;
477         }
478
479         if (core_idx < 0 || core_idx >= num_core) {
480                 _E("INVALID_PARAMETER(0x%08x) : invalid input parameter",
481                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
482                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
483         }
484
485         if (!cpu_freq) {
486                 _E("INVALID_PARAMETER(0x%08x) : invalid output parameter",
487                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
488                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
489         }
490
491         if (runtime_info_get_frequency_cpufreq(core_idx, "cur", cpu_freq)
492                         != RUNTIME_INFO_ERROR_NONE) {
493                 //LCOV_EXCL_START : system error
494                 _I("This system doesn't support cpufreq. Use cpuinfo instead.");
495
496                 switch (runtime_info_get_frequency_cpuinfo(core_idx, cpu_freq)) {
497                 case RUNTIME_INFO_ERROR_NONE:
498                         _I("Notice : it is max CPU frequency");
499                         break;
500                 case RUNTIME_INFO_ERROR_NO_DATA:
501                         _E("This system doesn't support MHz information in the cpuinfo");
502                         return RUNTIME_INFO_ERROR_NO_DATA;
503                 default:
504                         _E("Fail to get current CPU frequency");
505                         return RUNTIME_INFO_ERROR_IO_ERROR;
506                 };
507                 //LCOV_EXCL_STOP
508         }
509
510         return RUNTIME_INFO_ERROR_NONE;
511 }
512
513 API int runtime_info_get_processor_max_frequency(int core_idx, int *cpu_freq)
514 {
515         int num_core;
516
517         if (runtime_info_get_processor_count(&num_core)
518                         != RUNTIME_INFO_ERROR_NONE) {
519                 _E("runtime_info_get_processor_count is failed");
520                 return RUNTIME_INFO_ERROR_IO_ERROR;
521         }
522
523         if (core_idx < 0 || core_idx >= num_core) {
524                 _E("INVALID_PARAMETER(0x%08x) : invalid input parameter",
525                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
526                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
527         }
528
529         if (!cpu_freq) {
530                 _E("INVALID_PARAMETER(0x%08x) : invalid output parameter",
531                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
532                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
533         }
534
535         if (runtime_info_get_frequency_cpufreq(core_idx, "max", cpu_freq)
536                         != RUNTIME_INFO_ERROR_NONE) {
537                 //LCOV_EXCL_START : system error
538                 _I("This system doesn't support cpufreq. Use cpuinfo instead.");
539
540                 switch (runtime_info_get_frequency_cpuinfo(core_idx, cpu_freq)) {
541                 case RUNTIME_INFO_ERROR_NONE:
542                         break;
543                 case RUNTIME_INFO_ERROR_NO_DATA:
544                         _E("This system doesn't support MHz information in the cpuinfo");
545                         return RUNTIME_INFO_ERROR_NO_DATA;
546                 default:
547                         _E("Fail to get current CPU frequency");
548                         return RUNTIME_INFO_ERROR_IO_ERROR;
549                 };
550                 //LCOV_EXCL_STOP
551         }
552
553         return RUNTIME_INFO_ERROR_NONE;
554 }