Add new API for getting physical memory size
[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         return RUNTIME_INFO_ERROR_NONE;
200 }
201
202 API int runtime_info_get_process_memory_info(int *pid, int size, process_memory_info_s **info)
203 {
204         DBusMessageIter iter, iter_array, iter_struct;
205         int index, value;
206         int error;
207         process_memory_info_s *proc_info;
208         DBusMessage *replymsg;
209
210         if (!pid || size <= 0) {
211                 _E("INVALID_PARAMETER(0x%08x) : invalid input param",
212                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
213                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
214         }
215
216         if (!info) {
217                 _E("INVALID_PARAMETER(0x%08x) : invalid output param",
218                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
219                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
220         }
221
222         *info = NULL;
223
224         /* Get the needed information from resourced daemon using dbus */
225         replymsg = runtime_info_dbus_process_usage_info(pid, size, MEMORY_USAGE, &error);
226         if (!replymsg) {
227                 //LCOV_EXCL_START : system error
228                 _E("DBUS_METHOD_CALL: call to resourced not successful");
229                 return error;
230                 //LCOV_EXCL_STOP
231         }
232
233         /* Check if the message is an error message or not in expected
234          * format and return error value */
235         dbus_message_iter_init(replymsg, &iter);
236         if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INT32) {
237                 //LCOV_EXCL_START : system error
238                 dbus_message_iter_get_basic(&iter, &error);
239                 _E("DBUS_METHOD_CALL: call to resourced returned error message");
240                 dbus_message_unref(replymsg);
241                 return error;
242                 //LCOV_EXCL_STOP
243         } else if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
244                 //LCOV_EXCL_START : system error
245                 _E("DBUS_METHOD_CALL: received dbus message is not in expected format");
246                 dbus_message_unref(replymsg);
247                 return RUNTIME_INFO_ERROR_REMOTE_IO;
248                 //LCOV_EXCL_STOP
249         }
250
251         /* Populate the entries of info array using the data received from resourced */
252         *info = (process_memory_info_s *)malloc(size * sizeof(process_memory_info_s));
253         if (!info) {
254                 _E("OUT_OF_MEMORY(0x%08x)", RUNTIME_INFO_ERROR_OUT_OF_MEMORY);
255                 dbus_message_unref(replymsg);
256                 return RUNTIME_INFO_ERROR_OUT_OF_MEMORY;
257         }
258
259         dbus_message_iter_recurse(&iter, &iter_array);
260         for (index = 0; index < size; ++index) {
261                 proc_info = &(*info)[index];
262
263                 dbus_message_iter_recurse(&iter_array, &iter_struct);
264
265                 dbus_message_iter_get_basic(&iter_struct, &value);
266                 proc_info->vsz = value;
267                 dbus_message_iter_next(&iter_struct);
268                 dbus_message_iter_get_basic(&iter_struct, &value);
269                 proc_info->rss = value;
270                 dbus_message_iter_next(&iter_struct);
271                 dbus_message_iter_get_basic(&iter_struct, &value);
272                 proc_info->pss = value;
273                 dbus_message_iter_next(&iter_struct);
274                 dbus_message_iter_get_basic(&iter_struct, &value);
275                 proc_info->shared_clean = value;
276                 dbus_message_iter_next(&iter_struct);
277                 dbus_message_iter_get_basic(&iter_struct, &value);
278                 proc_info->shared_dirty = value;
279                 dbus_message_iter_next(&iter_struct);
280                 dbus_message_iter_get_basic(&iter_struct, &value);
281                 proc_info->private_clean = value;
282                 dbus_message_iter_next(&iter_struct);
283                 dbus_message_iter_get_basic(&iter_struct, &value);
284                 proc_info->private_dirty = value;
285
286                 dbus_message_iter_next(&iter_array);
287         }
288
289         dbus_message_unref(replymsg);
290         return RUNTIME_INFO_ERROR_NONE;
291 }
292
293 API int runtime_info_get_cpu_usage(runtime_cpu_usage_s *usage)
294 {
295         FILE *cpuinfo_fp;
296         char buf[256];
297         unsigned long long user, nice, system, idle, iowait, irq, softirq, total_uptime;
298
299         if (usage == NULL) {
300                 _E("INVALID_PARAMETER(0x%08x) : invalid output param",
301                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
302                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
303         }
304
305         cpuinfo_fp = fopen("/proc/stat", "r");
306         if (cpuinfo_fp == NULL) {
307                 _E("IO_ERROR(0x%08x) : failed to open file to read cpu usage",
308                                 RUNTIME_INFO_ERROR_IO_ERROR);
309                 return RUNTIME_INFO_ERROR_IO_ERROR;
310         }
311
312         while (fgets(buf, sizeof(buf), cpuinfo_fp) != NULL) {
313                 if (!strncmp(buf, "cpu ", 4) &&
314                     sscanf(buf+4, "%llu %llu %llu %llu %llu %llu %llu",
315                                 &user, &nice, &system, &idle,
316                                 &iowait, &irq, &softirq) == 7)
317                         break;
318         }
319         fclose(cpuinfo_fp);
320
321         total_uptime = user+nice+system+idle+iowait+irq+softirq;
322
323         usage->user = (double)user*100/total_uptime;
324         usage->nice = (double)nice*100/total_uptime;
325         usage->system = (double)system*100/total_uptime;
326         usage->iowait = (double)iowait*100/total_uptime;
327
328         return RUNTIME_INFO_ERROR_NONE;
329 }
330
331 API int runtime_info_get_process_cpu_usage(int *pid, int size, process_cpu_usage_s **usage)
332 {
333         DBusMessageIter iter, iter_array, iter_struct;
334         int index, value;
335         int error;
336         process_cpu_usage_s *proc_usage;
337         DBusMessage *replymsg;
338
339         if (!pid || size <= 0) {
340                 _E("INVALID_PARAMETER(0x%08x) : invalid input param",
341                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
342                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
343         }
344
345         if (!usage) {
346                 _E("INVALID_PARAMETER(0x%08x) : invalid output param",
347                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
348                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
349         }
350
351         *usage = NULL;
352
353         /* Get the needed information from resourced daemon using dbus */
354         replymsg = runtime_info_dbus_process_usage_info(pid, size, CPU_USAGE, &error);
355
356         if (!replymsg) {
357                 //LCOV_EXCL_START : system error
358                 _E("DBUS_METHOD_CALL: call to resourced not successful");
359                 return error;
360                 //LCOV_EXCL_STOP
361         }
362
363         /* Check if the message is an error message or not in expected format
364          * and return error value */
365         dbus_message_iter_init(replymsg, &iter);
366         if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INT32) {
367                 //LCOV_EXCL_START : system error
368                 dbus_message_iter_get_basic(&iter, &error);
369                 _E("DBUS_METHOD_CALL: call to resourced returned error message");
370                 dbus_message_unref(replymsg);
371                 return error;
372                 //LCOV_EXCL_STOP
373         } else if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
374                 //LCOV_EXCL_START : system error
375                 _E("DBUS_METHOD_CALL: received dbus message is not in expected format");
376                 dbus_message_unref(replymsg);
377                 return RUNTIME_INFO_ERROR_REMOTE_IO;
378                 //LCOV_EXCL_STOP
379         }
380
381         /* Populate the entries of info array using the data received from resourced */
382         *usage = (process_cpu_usage_s *)malloc(size * sizeof(process_cpu_usage_s));
383         if (!(*usage)) {
384                 //LCOV_EXCL_START : system error
385                 _E("OUT_OF_MEMORY(0x%08x)", RUNTIME_INFO_ERROR_OUT_OF_MEMORY);
386                 dbus_message_unref(replymsg);
387                 return RUNTIME_INFO_ERROR_OUT_OF_MEMORY;
388                 //LCOV_EXCL_STOP
389         }
390
391         dbus_message_iter_recurse(&iter, &iter_array);
392         for (index = 0; index < size; ++index) {
393                 proc_usage = &(*usage)[index];
394
395                 dbus_message_iter_recurse(&iter_array, &iter_struct);
396
397                 dbus_message_iter_get_basic(&iter_struct, &value);
398                 proc_usage->utime = value;
399                 dbus_message_iter_next(&iter_struct);
400                 dbus_message_iter_get_basic(&iter_struct, &value);
401                 proc_usage->stime = value;
402
403                 dbus_message_iter_next(&iter_array);
404         }
405
406         dbus_message_unref(replymsg);
407         return RUNTIME_INFO_ERROR_NONE;
408 }
409
410 API int runtime_info_get_processor_count(int *num_core)
411 {
412         FILE *cpuinfo_fp;
413         int buf;
414         int result;
415
416         if (!num_core) {
417                 _E("INVALID_PARAMETER(0x%08x) : invalid output parameter",
418                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
419                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
420         }
421
422         cpuinfo_fp = fopen("/sys/devices/system/cpu/possible", "r");
423         if (cpuinfo_fp == NULL) {
424                 //LCOV_EXCL_START : system error
425                 _E("IO_ERROR(0x%08x) : failed to open file to read cpu information",
426                                 RUNTIME_INFO_ERROR_IO_ERROR);
427                 return RUNTIME_INFO_ERROR_IO_ERROR;
428                 //LCOV_EXCL_STOP
429         }
430
431         if (!fscanf(cpuinfo_fp, "%d-%d", &buf, &result)) {
432                 //LCOV_EXCL_START : system error
433                 _E("IO_ERROR(0x%08x) : there is no information in the system file",
434                                 RUNTIME_INFO_ERROR_IO_ERROR);
435                 fclose(cpuinfo_fp);
436                 return RUNTIME_INFO_ERROR_IO_ERROR;
437                 //LCOV_EXCL_STOP
438         }
439
440         *num_core = result + 1;
441
442         fclose(cpuinfo_fp);
443         return RUNTIME_INFO_ERROR_NONE;
444 }
445
446 API int runtime_info_get_processor_current_frequency(int core_idx, int *cpu_freq)
447 {
448         int num_core;
449
450         if (runtime_info_get_processor_count(&num_core)
451                         != RUNTIME_INFO_ERROR_NONE) {
452                 _E("runtime_info_get_processor_count is failed");
453                 return RUNTIME_INFO_ERROR_IO_ERROR;
454         }
455
456         if (core_idx < 0 || core_idx >= num_core) {
457                 _E("INVALID_PARAMETER(0x%08x) : invalid input parameter",
458                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
459                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
460         }
461
462         if (!cpu_freq) {
463                 _E("INVALID_PARAMETER(0x%08x) : invalid output parameter",
464                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
465                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
466         }
467
468         if (runtime_info_get_frequency_cpufreq(core_idx, "cur", cpu_freq)
469                         != RUNTIME_INFO_ERROR_NONE) {
470                 //LCOV_EXCL_START : system error
471                 _I("This system doesn't support cpufreq. Use cpuinfo instead.");
472
473                 switch (runtime_info_get_frequency_cpuinfo(core_idx, cpu_freq)) {
474                 case RUNTIME_INFO_ERROR_NONE:
475                         _I("Notice : it is max CPU frequency");
476                         break;
477                 case RUNTIME_INFO_ERROR_NO_DATA:
478                         _E("This system doesn't support MHz information in the cpuinfo");
479                         return RUNTIME_INFO_ERROR_NO_DATA;
480                 default:
481                         _E("Fail to get current CPU frequency");
482                         return RUNTIME_INFO_ERROR_IO_ERROR;
483                 };
484                 //LCOV_EXCL_STOP
485         }
486
487         return RUNTIME_INFO_ERROR_NONE;
488 }
489
490 API int runtime_info_get_processor_max_frequency(int core_idx, int *cpu_freq)
491 {
492         int num_core;
493
494         if (runtime_info_get_processor_count(&num_core)
495                         != RUNTIME_INFO_ERROR_NONE) {
496                 _E("runtime_info_get_processor_count is failed");
497                 return RUNTIME_INFO_ERROR_IO_ERROR;
498         }
499
500         if (core_idx < 0 || core_idx >= num_core) {
501                 _E("INVALID_PARAMETER(0x%08x) : invalid input parameter",
502                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
503                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
504         }
505
506         if (!cpu_freq) {
507                 _E("INVALID_PARAMETER(0x%08x) : invalid output parameter",
508                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
509                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
510         }
511
512         if (runtime_info_get_frequency_cpufreq(core_idx, "max", cpu_freq)
513                         != RUNTIME_INFO_ERROR_NONE) {
514                 //LCOV_EXCL_START : system error
515                 _I("This system doesn't support cpufreq. Use cpuinfo instead.");
516
517                 switch (runtime_info_get_frequency_cpuinfo(core_idx, cpu_freq)) {
518                 case RUNTIME_INFO_ERROR_NONE:
519                         break;
520                 case RUNTIME_INFO_ERROR_NO_DATA:
521                         _E("This system doesn't support MHz information in the cpuinfo");
522                         return RUNTIME_INFO_ERROR_NO_DATA;
523                 default:
524                         _E("Fail to get current CPU frequency");
525                         return RUNTIME_INFO_ERROR_IO_ERROR;
526                 };
527                 //LCOV_EXCL_STOP
528         }
529
530         return RUNTIME_INFO_ERROR_NONE;
531 }
532
533 API int runtime_info_get_physical_memory_size(int *size)
534 {
535         char buf[256];
536         unsigned long value;
537         int sum;
538         FILE *fp = fopen("/proc/zoneinfo", "r");
539         if (!fp) {
540                 _E("IO_ERROR(0x%08x) : failed to open file to read memory size",
541                                 RUNTIME_INFO_ERROR_IO_ERROR);
542                 return RUNTIME_INFO_ERROR_IO_ERROR;
543         }
544
545         if (!size) {
546                 _E("INVALID PARAMETER(0x%08x) : invalid output parameter",
547                                 RUNTIME_INFO_ERROR_INVALID_PARAMETER);
548                 fclose(fp);
549                 return RUNTIME_INFO_ERROR_INVALID_PARAMETER;
550         }
551
552         sum = 0;
553         while (fgets(buf, sizeof(buf), fp) != NULL)
554                 if (sscanf(buf, " spanned %lu", &value) == 1)
555                         sum += value;
556         *size = pagetoKiB(sum);
557
558         fclose(fp);
559
560         return RUNTIME_INFO_ERROR_NONE;
561 }