15 #include <sys/types.h>
16 #include <sys/utsname.h>
18 #include <sys/resource.h>
24 #define UNW_LOCAL_ONLY
25 #include <libunwind.h>
26 #include <elfutils/libdwfl.h>
29 static const afw_utf8_t impl_s_no_memory_for_backtrace =
35 static const afw_utf8_t impl_s_general_description =
111 static const afw_utf8_t impl_s_resourceLimits =
142 #ifdef __EXAMPLE_FOR_CREATING_WITH_CB_WRAPPER_
145 impl_s_<objectId>_description =
157 impl_s_os_description =
165 impl_get_<objectId>_object_cb(
176 &afw_s__AdaptiveSystemInfo_,
180 string, &impl_s_<objectId>_description, xctx);
186 &impl_s_os, &impl_s_linux, xctx);
188 &impl_s_os, &afw_s_label,
189 single,
string, &impl_s_os_label, xctx);
191 &impl_s_os, &afw_s_description,
192 single,
string, &impl_s_os_description, xctx);
201 impl_get_resourceLimits_object_cb(
207 struct rlimit *limits;
214 &afw_s__AdaptiveSystemInfo_,
215 &impl_s_resourceLimits,
219 result, &afw_s_description,
220 "Resource Limits for the calling process.",
226 rc = getrlimit(RLIMIT_CPU, limits);
229 &impl_s_rl_cpu, limits->rlim_cur, xctx);
231 &impl_s_rl_cpu, &afw_s_label,
"CPU Time", xctx);
233 &impl_s_rl_cpu, &afw_s_description,
234 "The maximum amount of CPU time the process can use. If it runs for longer than this, it gets a signal: SIGXCPU. The value is measured in seconds.",
238 rc = getrlimit(RLIMIT_FSIZE, limits);
241 &impl_s_rl_fsize, limits->rlim_cur, xctx);
243 &impl_s_rl_fsize, &afw_s_label,
"File Size", xctx);
245 &impl_s_rl_fsize, &afw_s_description,
246 "The maximum size of file the process can create. Trying to write a larger file causes a signal: SIGXFSZ.",
250 rc = getrlimit(RLIMIT_STACK, limits);
253 &impl_s_rl_stack, limits->rlim_cur, xctx);
255 &impl_s_rl_stack, &afw_s_label,
"Stack Size", xctx);
257 &impl_s_rl_stack, &afw_s_description,
258 "The maximum stack size for the process. If the process tries to extend its stack past this size, it gets a SIGSEGV signal",
262 rc = getrlimit(RLIMIT_CORE, limits);
265 &impl_s_rl_core, limits->rlim_cur, xctx);
267 &impl_s_rl_core, &afw_s_label,
"Core Size", xctx);
269 &impl_s_rl_core, &afw_s_description,
270 "The maximum size core file that this process can create. If the process terminates and would dump a core file larger than this, then no core file is created. So setting this limit to zero prevents core files from ever being created.",
274 rc = getrlimit(RLIMIT_RSS, limits);
277 &impl_s_rl_rss, limits->rlim_cur, xctx);
279 &impl_s_rl_rss, &afw_s_label,
"Physical Memory", xctx);
281 &impl_s_rl_rss, &afw_s_description,
282 "The maximum amount of physical memory that this process should get. This parameter is a guide for the system’s scheduler and memory allocator; the system may give the process more memory when there is a surplus.",
286 rc = getrlimit(RLIMIT_MEMLOCK, limits);
289 &impl_s_rl_memlock, limits->rlim_cur, xctx);
291 &impl_s_rl_memlock, &afw_s_label,
"Locked Physical Memory", xctx);
293 &impl_s_rl_memlock, &afw_s_description,
294 "The maximum amount of memory that can be locked into physical memory (so it will never be paged out).",
298 rc = getrlimit(RLIMIT_NPROC, limits);
301 &impl_s_rl_nproc, limits->rlim_cur, xctx);
303 &impl_s_rl_nproc, &afw_s_label,
"Processes", xctx);
305 &impl_s_rl_nproc, &afw_s_description,
306 "The maximum number of processes that can be created with the same user ID. If you have reached the limit for your user ID, fork will fail with EAGAIN.",
310 #if defined(RLIMIT_NOFILE)
311 rc = getrlimit(RLIMIT_NOFILE, limits);
313 rc = getrlimit(RLIMIT_OFILE, limits);
317 &impl_s_rl_ofile, limits->rlim_cur, xctx);
319 &impl_s_rl_ofile, &afw_s_label,
"Open Files", xctx);
321 &impl_s_rl_ofile, &afw_s_description,
322 "The maximum number of files that the process can open. If it tries to open more files than this, its open attempt fails with errno EMFILE.",
327 rc = getrlimit(RLIMIT_DATA, limits);
329 rc = getrlimit(RLIMIT_AS, limits);
333 &impl_s_rl_as, limits->rlim_cur, xctx);
335 &impl_s_rl_as, &afw_s_label,
"Total Memory", xctx);
337 &impl_s_rl_as, &afw_s_description,
338 "The maximum size of total memory that this process should get. If the process tries to allocate more memory beyond this amount with, for example, brk, malloc, mmap or sbrk, the allocation function fails.",
346 impl_get_resourceUsage_object_cb(
352 struct rusage *usage;
359 &afw_s__AdaptiveSystemInfo_,
360 &impl_s_resourceUsage,
364 result, &afw_s_description,
365 "Resource usage statistics for the calling process, which is the sum of resources used by all threads in the process.",
370 rc = getrusage(RUSAGE_SELF, usage);
373 &impl_s_ru_utime,
true,
374 0, 0, 0, usage->ru_utime.tv_sec, usage->ru_utime.tv_usec,
377 &impl_s_ru_utime, &afw_s_label,
"User Time", xctx);
379 &impl_s_ru_utime, &afw_s_description,
380 "Time spent executing user instructions.",
384 &impl_s_ru_stime,
true,
385 0, 0, 0, usage->ru_stime.tv_sec, usage->ru_stime.tv_usec,
388 &impl_s_ru_stime, &afw_s_label,
"System Time", xctx);
390 &impl_s_ru_stime, &afw_s_description,
391 "Time spent in operating system code on behalf of this processes.",
395 &impl_s_ru_maxrss, usage->ru_maxrss, xctx);
397 &impl_s_ru_maxrss, &afw_s_label,
"Max Resident Size", xctx);
399 &impl_s_ru_maxrss, &afw_s_description,
400 "The maximum resident set size used, in kilobytes. That is, the maximum number of kilobytes of physical memory that this process used simultaneously.",
404 &impl_s_ru_ixrss, usage->ru_ixrss, xctx);
406 &impl_s_ru_ixrss, &afw_s_label,
"Shared Memory Size", xctx);
408 &impl_s_ru_ixrss, &afw_s_description,
409 "An integral value expressed in kilobytes times ticks of execution, which indicates the amount of memory used by text that was shared with other processes.",
413 &impl_s_ru_idrss, usage->ru_idrss, xctx);
415 &impl_s_ru_idrss, &afw_s_label,
"Unshared Data Size", xctx);
417 &impl_s_ru_idrss, &afw_s_description,
418 "An integral value expressed the same way, which is the amount of unshared memory used for data.",
422 &impl_s_ru_isrss, usage->ru_isrss, xctx);
424 &impl_s_ru_isrss, &afw_s_label,
"Unshared Stack Size", xctx);
426 &impl_s_ru_isrss, &afw_s_description,
427 "An integral value expressed the same way, which is the amount of unshared memory used for stack space.",
431 &impl_s_ru_minflt, usage->ru_minflt, xctx);
433 &impl_s_ru_minflt, &afw_s_label,
"Page Reclaims", xctx);
435 &impl_s_ru_minflt, &afw_s_description,
436 "The number of page faults which were serviced without requiring any I/O.",
440 &impl_s_ru_majflt, usage->ru_majflt, xctx);
442 &impl_s_ru_majflt, &afw_s_label,
"Page Faults", xctx);
444 &impl_s_ru_majflt, &afw_s_description,
445 "The number of page faults which were serviced by doing I/O.",
449 &impl_s_ru_nswap, usage->ru_nswap, xctx);
451 &impl_s_ru_nswap, &afw_s_label,
"Swaps", xctx);
453 &impl_s_ru_nswap, &afw_s_description,
454 "The number of times this processes was swapped entirely out of main memory.",
458 &impl_s_ru_inblock, usage->ru_inblock, xctx);
460 &impl_s_ru_inblock, &afw_s_label,
"Disk Reads", xctx);
462 &impl_s_ru_inblock, &afw_s_description,
463 "The number of times the file system had to read from the disk on behalf of this processes.",
467 &impl_s_ru_oublock, usage->ru_oublock, xctx);
469 &impl_s_ru_oublock, &afw_s_label,
"Disk Writes", xctx);
471 &impl_s_ru_oublock, &afw_s_description,
472 "The number of times the file system had to write to the disk on behalf of this processes.",
476 &impl_s_ru_msgsnd, usage->ru_msgsnd, xctx);
478 &impl_s_ru_msgsnd, &afw_s_label,
"Messages Sent", xctx);
480 &impl_s_ru_msgsnd, &afw_s_description,
481 "Number of IPC messages sent.",
485 &impl_s_ru_msgrcv, usage->ru_msgrcv, xctx);
487 &impl_s_ru_msgrcv, &afw_s_label,
"Messages Received", xctx);
489 &impl_s_ru_msgrcv, &afw_s_description,
490 "Number of IPC messages received.",
494 &impl_s_ru_nsignals, usage->ru_nsignals, xctx);
496 &impl_s_ru_nsignals, &afw_s_label,
"Signals Received", xctx);
498 &impl_s_ru_nsignals, &afw_s_description,
499 "Number of signals received.",
503 &impl_s_ru_nvcsw, usage->ru_nvcsw, xctx);
505 &impl_s_ru_nvcsw, &afw_s_label,
"Voluntary Context Switches", xctx);
507 &impl_s_ru_nvcsw, &afw_s_description,
508 "The number of times this processes voluntarily invoked a context switch (usually to wait for some service).",
512 &impl_s_ru_nivcsw, usage->ru_nivcsw, xctx);
514 &impl_s_ru_nivcsw, &afw_s_label,
"Involuntary Context Switches", xctx);
516 &impl_s_ru_nivcsw, &afw_s_description,
517 "The number of times an involuntary context switch took place (because a time slice expired, or another process of higher priority was scheduled).",
530 struct utsname *uname_s;
537 &afw_s__AdaptiveSystemInfo_,
541 string, &impl_s_general_description, xctx);
549 &impl_s_sysname, uname_s->sysname, xctx);
551 &impl_s_sysname, &afw_s_label,
"System Name", xctx);
553 &impl_s_sysname, &afw_s_description,
"This is the name of the operating system in use.", xctx);
556 &impl_s_nodename, uname_s->nodename, xctx);
558 &impl_s_nodename, &afw_s_label,
"Node Name", xctx);
560 &impl_s_nodename, &afw_s_description,
"This is the host name of this particular computer. In the GNU C Library, the value is the same as that returned by gethostname.", xctx);
563 &impl_s_release, uname_s->release, xctx);
565 &impl_s_release, &afw_s_label,
"Release", xctx);
567 &impl_s_release, &afw_s_description,
"This is the current release level of the operating system implementation.", xctx);
570 &impl_s_version, uname_s->version, xctx);
572 &impl_s_version, &afw_s_label,
"Version", xctx);
574 &impl_s_version, &afw_s_description,
"This is the current version level within the release of the operating system.", xctx);
577 &impl_s_machine, uname_s->machine, xctx);
579 &impl_s_machine, &afw_s_label,
"Machine", xctx);
581 &impl_s_machine, &afw_s_description,
"This is a description of the type of hardware that is in use.", xctx);
593 &impl_s_domainname, &afw_s_label,
"Domain Name", xctx);
595 &impl_s_domainname, &afw_s_description,
"This is the NIS or YP domain name. It is the same value returned by getdomainname.", xctx);
615 &log_factory->log_type,
619 object = impl_create_general_object(xctx);
624 &afw_s__AdaptiveSystemInfo_, &impl_s_resourceUsage,
625 impl_get_resourceUsage_object_cb, NULL,
false, xctx);
629 &afw_s__AdaptiveSystemInfo_, &impl_s_resourceLimits,
630 impl_get_resourceLimits_object_cb, NULL,
false, xctx);
645 return &impl_dso_suffix;
668 void * callstack[128];
683 if (max_backtrace == 0)
return NULL;
684 if (max_backtrace < 0) max = 100;
685 else if (max_backtrace > 99) max = 100;
686 else max = max_backtrace + 1;
696 len = (max + 10) * 100;
698 if (!s)
return &impl_s_no_memory_for_backtrace;
700 if (!trace)
return &impl_s_no_memory_for_backtrace;
705 frames = backtrace(callstack, 128);
709 strs = backtrace_symbols(callstack, frames);
713 for (i = 0; i < frames; i++) {
714 wlen = snprintf(s, len,
"%s\n", strs[i]);
715 if (wlen < 0 || (
afw_size_t)wlen > len)
break;
722 trace->len = s - trace->s;
734 char *debuginfo_path = NULL;
735 Dwfl_Callbacks callbacks = {
736 .find_elf = dwfl_linux_proc_find_elf,
737 .find_debuginfo = dwfl_standard_find_debuginfo,
738 .debuginfo_path = &debuginfo_path,
761 if (max_backtrace == 0)
return NULL;
762 if (max_backtrace < 0) max = 100;
763 else if (max_backtrace > 99) max = 100;
764 else max = max_backtrace + 1;
774 len = (max + 10) * 100;
776 if (!s)
return &impl_s_no_memory_for_backtrace;
778 if (!trace)
return &impl_s_no_memory_for_backtrace;
783 dwfl = dwfl_begin(&callbacks);
785 return &impl_s_no_memory_for_backtrace;
788 dwfl_linux_proc_report(dwfl, getpid());
789 dwfl_report_end(dwfl, NULL, NULL);
792 rc = unw_init_local(&cursor, &uc);
799 for (i = 0; unw_step(&cursor) > 0 && i < max; i++) {
800 unw_word_t offset, ip, sp;
803 unw_get_reg(&cursor, UNW_REG_IP, &ip);
804 unw_get_reg(&cursor, UNW_REG_SP, &sp);
809 wlen = snprintf(s, len,
"0x%012lx in ", ip);
810 if (wlen < 0 || (
afw_size_t)wlen > len)
break;
814 addr = (uintptr_t)(ip-4);
815 module = dwfl_addrmodule(dwfl, addr);
816 function_name = dwfl_module_addrname(module, addr);
817 line = dwfl_getsrc(dwfl, addr);
821 const char *filename = dwfl_lineinfo(line, &addr, &nline, NULL, NULL, NULL);
823 wlen = snprintf(s, len,
"%s at %s:%d\n",
824 function_name, strrchr(filename,
'/') + 1, nline);
825 if (wlen < 0 || (
afw_size_t)wlen > len)
break;
829 if (unw_get_proc_name(&cursor, sym,
sizeof(sym), &offset) == 0) {
830 wlen = snprintf(s, len,
"%s at +0x%lx\n",
832 if (wlen < 0 || (
afw_size_t)wlen > len)
break;
836 wlen = snprintf(s, len,
"(symbol missing)\n");
837 if (wlen < 0 || (
afw_size_t)wlen > len)
break;
844 trace->len = s - trace->s;
Adaptive Framework Core API.
AFW_DEFINE(const afw_object_t *)
afw_object_set_property_as_integer(const afw_object_t *object, const afw_utf8_t *property_name, afw_integer_t internal, afw_xctx_t *xctx)
Set property function for data type integer values.
afw_object_set_property_as_string(const afw_object_t *object, const afw_utf8_t *property_name, const afw_utf8_t *internal, afw_xctx_t *xctx)
Set property function for data type string values.
#define AFW_UTF8_LITERAL(A_STRING)
String literal initializer.
enum afw_error_code_e afw_error_code_t
unsigned char afw_byte_t
A byte of memory (unsigned).
apr_uint32_t afw_uint32_t
32-bit unsigned integer.
char afw_utf8_octet_t
8 bits of utf-8 codepoint.
apr_size_t afw_size_t
size_t.
void afw_environment_register_log_type(const afw_utf8_t *log_type, const afw_log_factory_t *log_factory, afw_xctx_t *xctx)
Register an log factory.
afw_object_set_property_as_dayTimeDuration_from_parts(const afw_object_t *instance, const afw_utf8_t *property_name, afw_boolean_t is_positive, int days, int hours, int minutes, int seconds, int microseconds, afw_xctx_t *xctx)
Set a dayTimeDuration property from parts.
#define afw_object_create(p, xctx)
Create an empty unmanaged object in memory.
afw_object_set_property_as_string_from_utf8_z(const afw_object_t *instance, const afw_utf8_t *property_name, const afw_utf8_z_t *string_z, afw_xctx_t *xctx)
Set an string property from utf8_z.
afw_os_environment_initialize(afw_xctx_t *xctx)
afw_os environment initialize
afw_os_backtrace(afw_error_code_t code, int max_backtrace, afw_xctx_t *xctx)
Provide a backtrace if possible.
afw_os_get_dso_suffix()
Return the suffix appended to dso file names for this system.
afw_uint32_t afw_os_get_pid()
Return a process id or similar number.
const afw_log_factory_t * afw_os_log_factory_get()
Get the factory for OS log.
#define afw_pool_get_apr_pool(instance)
Call method get_apr_pool of interface afw_pool.
#define afw_pool_calloc_type(instance, type, xctx)
Macro to allocate cleared memory to hold type in pool.
afw_runtime_env_set_object(const afw_object_t *object, afw_boolean_t overwrite, afw_xctx_t *xctx)
Set an object pointer in the environment's runtime objects.
afw_runtime_env_set_object_cb_wrapper(const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, afw_runtime_object_wrapper_p_cb_t callback, void *data, afw_boolean_t overwrite, afw_xctx_t *xctx)
Set environment object accessed via callback.
#define afw_xctx_calloc_type(type, xctx)
Macro to allocate cleared memory to hold type in xctx's pool.
Interface afw_log_factory public struct.
Interface afw_object public struct.
Interface afw_pool public struct.
NFC normalized UTF-8 string.
Interface afw_xctx public struct.