@cryptotaxi247 / netdata-1 / commits / 06bf6631a

apps.plugin for windows (#18594)

* apps.plugin uses a hashtable for pids; apps.plugin pids sortlist cleanup * struct pid_stat now uses aral * structures cleanup * remove limitation on command name length * fix log * process tree grouping which automatically groups the processes based on the process tree * cleanup * revert accidental changes * fix debug logs for STRING pointers * moved perflib to libnetdata; fixed apps.plugin to accept windows specific functions; not yet working on windows * fix for linux * basic structure for perflib collection * control features per O/S * split aggregations * isolate user and group targets per O/S * gather all O/S functions together * fix for windows * virtualized all process variables * fixed macro; process name extracted from cmdline in a better way * finished modularizing the whole code * fix compilation on windows * fix compilation on macos * fix format in debug statement * windows collector for apps.plugin * windows processes CPU * fix process name * fix macos * fix freebsd * make it run under clion windows * cpu utilization in NANOSECONDCORES * windows cpu utilization in nanosecondcores * memory utilization is internally in bytes * exclude pid 0 on windows * remove the updated flag too * reset the processing flags at the beginning * fixed exited processes processing * fixes for exited children * fix for macos * fix for freebsd * handles are now a type of fd * fix fds on windows * macos and freebsd have logical I/O, not physical I/O * freebsd now reports i/o bytes, not blocks * I/O calls are now I/O ops * fix uptime in windows * get more friendly windows process names * added parents to function; added orchestrators and aggregators; added mutex to processes function * added pid name, when it is available * documentation * more code cleanup * fixes for windows * fix infinite pool * add name to processes function, when available * break infinite loop when processes are linked in a loop * parent-child loop detection earlier * debug loops * debug loops * debug loops * debug loops * debug loops * debug loops * debug loops * debug loops * debug loops * fixed parents loops * do not errno in loops * cosmetic changes * fixed exited pids processing * simplified exited pids processing * simplified exited pids processing again * code rearrange on users and groups * fix freebsd; new tree process chart name and label * pid 0 is an aggregator for all operating systems * System becomes kernel * Update src/collectors/apps.plugin/README.md Co-authored-by: Fotis Voutsas <fotis@netdata.cloud> * fixed typo * fixed bug in procfile parsing when multiple opening and closing brackets appear * removed trailing spaces from cmdline * fixed orchestrators * merged tree and app_groups groupings * updated app_groups.conf * added docker-init --------- Co-authored-by: Costa Tsaousis <costa@Costas-Macbook-Pro.local> Co-authored-by: Costa Tsaousis <costa@MacBookPro.plaka> Co-authored-by: Fotis Voutsas <fotis@netdata.cloud>

Costa Tsaousis committed Sep 30, 2024 at 17:30 UTC 06bf6631a0dc3cc1da0f6c6873ea158beab78624
55 files changed +9836 -8214
CMakeLists.txt
+21 -15
@@ -125,6 +125,8 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "CYGWIN" OR "${CMAKE_SYSTEM_NAME}" STREQU
125 add_definitions(-D_GNU_SOURCE)
126
127 if($ENV{CLION_IDE})
128 + set(RUN_UNDER_CLION True)
129 +
130 # clion needs these to find the includes
131 if("${CMAKE_SYSTEM_NAME}" STREQUAL "MSYS" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
132 if("$ENV{MSYSTEM}" STREQUAL "MSYS")
@@ -158,7 +160,7 @@ option(ENABLE_DBENGINE "Enable dbengine metrics storage" True)
160 option(ENABLE_PLUGIN_GO "Enable metric collectors written in Go" ${DEFAULT_FEATURE_STATE})
161 option(ENABLE_PLUGIN_PYTHON "Enable metric collectors written in Python" ${DEFAULT_FEATURE_STATE})
162
161 -cmake_dependent_option(ENABLE_PLUGIN_APPS "Enable per-process resource usage monitoring" ${DEFAULT_FEATURE_STATE} "NOT OS_WINDOWS" False)
163 +cmake_dependent_option(ENABLE_PLUGIN_APPS "Enable per-process resource usage monitoring" ${DEFAULT_FEATURE_STATE} "OS_LINUX OR OS_FREEBSD OR OS_MACOS OR OS_WINDOWS" False)
164 cmake_dependent_option(ENABLE_PLUGIN_CHARTS "Enable metric collectors written in Bash" ${DEFAULT_FEATURE_STATE} "NOT OS_WINDOWS" False)
165 cmake_dependent_option(ENABLE_PLUGIN_CUPS "Enable CUPS monitoring" ${DEFAULT_FEATURE_STATE} "NOT OS_WINDOWS" False)
166
@@ -763,6 +765,10 @@ set(LIBNETDATA_FILES
765 src/libnetdata/os/close_range.h
766 src/libnetdata/os/setproctitle.c
767 src/libnetdata/os/setproctitle.h
768 + src/libnetdata/os/windows-perflib/perflib.c
769 + src/libnetdata/os/windows-perflib/perflib.h
770 + src/libnetdata/os/windows-perflib/perflib-names.c
771 + src/libnetdata/os/windows-perflib/perflib-dump.c
772 src/libnetdata/paths/paths.c
773 src/libnetdata/paths/paths.h
774 src/libnetdata/json/json-c-parser-inline.c
@@ -796,6 +802,8 @@ set(LIBNETDATA_FILES
802 src/libnetdata/os/timestamps.h
803 src/libnetdata/parsers/entries.c
804 src/libnetdata/parsers/entries.h
805 + src/libnetdata/sanitizers/chart_id_and_name.c
806 + src/libnetdata/sanitizers/chart_id_and_name.h
807 )
808
809 if(ENABLE_PLUGIN_EBPF)
@@ -1427,12 +1435,8 @@ set(WINDOWS_PLUGIN_FILES
1435 src/collectors/windows.plugin/GetSystemUptime.c
1436 src/collectors/windows.plugin/GetSystemRAM.c
1437 src/collectors/windows.plugin/GetSystemCPU.c
1430 - src/collectors/windows.plugin/perflib.c
1431 - src/collectors/windows.plugin/perflib.h
1438 src/collectors/windows.plugin/perflib-rrd.c
1439 src/collectors/windows.plugin/perflib-rrd.h
1434 - src/collectors/windows.plugin/perflib-names.c
1435 - src/collectors/windows.plugin/perflib-dump.c
1440 src/collectors/windows.plugin/perflib-storage.c
1441 src/collectors/windows.plugin/perflib-processor.c
1442 src/collectors/windows.plugin/perflib-thermalzone.c
@@ -1883,19 +1887,21 @@ if(ENABLE_PLUGIN_APPS)
1887 src/collectors/apps.plugin/apps_targets.c
1888 src/collectors/apps.plugin/apps_users_and_groups.c
1889 src/collectors/apps.plugin/apps_output.c
1886 - src/collectors/apps.plugin/apps_proc_pid_status.c
1887 - src/collectors/apps.plugin/apps_proc_pid_limits.c
1888 - src/collectors/apps.plugin/apps_proc_pid_stat.c
1889 - src/collectors/apps.plugin/apps_proc_pid_cmdline.c
1890 - src/collectors/apps.plugin/apps_proc_pid_io.c
1891 - src/collectors/apps.plugin/apps_proc_stat.c
1892 - src/collectors/apps.plugin/apps_proc_pid_fd.c
1893 - src/collectors/apps.plugin/apps_proc_pids.c
1894 - src/collectors/apps.plugin/apps_proc_meminfo.c
1890 + src/collectors/apps.plugin/apps_pid_files.c
1891 + src/collectors/apps.plugin/apps_pid.c
1892 + src/collectors/apps.plugin/apps_aggregations.c
1893 + src/collectors/apps.plugin/apps_os_linux.c
1894 + src/collectors/apps.plugin/apps_os_freebsd.c
1895 + src/collectors/apps.plugin/apps_os_macos.c
1896 + src/collectors/apps.plugin/apps_os_windows.c
1897 + src/collectors/apps.plugin/apps_incremental_collection.c
1898 )
1899
1900 add_executable(apps.plugin ${APPS_PLUGIN_FILES})
1898 - target_link_libraries(apps.plugin libnetdata ${CAP_LIBRARIES})
1901 +
1902 + target_link_libraries(apps.plugin libnetdata ${CAP_LIBRARIES}
1903 + "$<$<BOOL:${OS_WINDOWS}>:Version>")
1904 +
1905 target_include_directories(apps.plugin PRIVATE ${CAP_INCLUDE_DIRS})
1906 target_compile_options(apps.plugin PRIVATE ${CAP_CFLAGS_OTHER})
1907
packaging/cmake/config.cmake.h.in
+2
@@ -169,6 +169,8 @@
169 #cmakedefine HAVE_LIBYAML
170 #cmakedefine HAVE_LIBMNL
171
172 +#cmakedefine RUN_UNDER_CLION
173 +
174 // /* Enable GNU extensions on systems that have them. */
175 // #ifndef _GNU_SOURCE
176 // # define _GNU_SOURCE 1
packaging/utils/compile-on-windows.sh
+1
@@ -66,6 +66,7 @@ then
66 -DENABLE_ML=On \
67 -DENABLE_BUNDLED_JSONC=On \
68 -DENABLE_BUNDLED_PROTOBUF=Off \
69 + -DENABLE_PLUGIN_APPS=On \
70 ${NULL}
71 fi
72
src/collectors/apps.plugin/README.md
+49 -24
@@ -7,30 +7,57 @@ learn_topic_type: "References"
7 learn_rel_path: "Integrations/Monitor/System metrics"
8 -->
9
10 -# Application monitoring (apps.plugin)
10 +# Applications monitoring (apps.plugin)
11
12 -`apps.plugin` breaks down system resource usage to **processes**, **users** and **user groups**.
13 -It is enabled by default on every Netdata installation.
12 +`apps.plugin` monitors the resources utilization of all processes running.
13
15 -To achieve this task, it iterates through the whole process tree, collecting resource usage information
16 -for every process found running.
14 +## Process Aggregation and Grouping
15
18 -Since Netdata needs to present this information in charts and track them through time,
19 -instead of presenting a `top` like list, `apps.plugin` uses a pre-defined list of **process groups**
20 -to which it assigns all running processes. This list is customizable via `apps_groups.conf`, and Netdata
21 -ships with a good default for most cases (to edit it on your system run `/etc/netdata/edit-config apps_groups.conf`).
16 +`apps.plugin` aggregates processes in three distinct ways to provide a more insightful
17 +breakdown of resource utilization:
18
23 -So, `apps.plugin` builds a process tree (much like `ps fax` does in Linux), and groups
19 + - **Tree** or **Category**: Grouped by their position in the process tree.
20 + This is customizable and allows aggregation by process managers and individual
21 + processes of interest. Allows also renaming the processes for presentation purposes.
22 +
23 + - **User**: Grouped by the effective user (UID) under which the processes run.
24 +
25 + - **Group**: Grouped by the effective group (GID) under which the processes run.
26 +
27 + ## Short-Lived Process Handling
28 +
29 +`apps.plugin` accounts for resource utilization of both running and exited processes,
30 +capturing the impact of processes that spawn short-lived subprocesses, such as shell
31 +scripts that fork hundreds or thousands of times per second. So, although processes
32 +may spawn short lived sub-processes, `apps.plugin` will aggregate their resources
33 +utilization providing a holistic view of how resources are shared among the processes.
34 +
35 +## Charts sections
36 +
37 +To provide more valuable insights, apps.plugin aggregates individual processes in several ways.
38 +Each type of aggregation is presented as a different section on the dashboard.
39 +
40 +### Custom Process Groups (Apps)
41 +
42 +In this section, apps.plugin summarizes the resources consumed by all processes, grouped based
43 +on the groups provided in `/etc/netdata/apps_groups.conf`. You can edit this file using our [`edit-config`](docs/netdata-agent/configuration/README.md) script.
44 +
45 +For this section, `apps.plugin` builds a process tree (much like `ps fax` does in Linux), and groups
46 processes together (evaluating both child and parent processes) so that the result is always a list with
47 a predefined set of members (of course, only process groups found running are reported).
48
49 > If you find that `apps.plugin` categorizes standard applications as `other`, we would be
50 > glad to accept pull requests improving the defaults shipped with Netdata in `apps_groups.conf`.
51
30 -Unlike traditional process monitoring tools (like `top`), `apps.plugin` is able to account the resource
31 -utilization of exit processes. Their utilization is accounted at their currently running parents.
32 -So, `apps.plugin` is perfectly able to measure the resources used by shell scripts and other processes
33 -that fork/spawn other short-lived processes hundreds of times per second.
52 +### By User (Users)
53 +
54 +In this section, apps.plugin summarizes the resources consumed by all processes, grouped by the
55 +effective user under which each process runs.
56 +
57 +### By User Group (Groups)
58 +
59 +In this section, apps.plugin summarizes the resources consumed by all processes, grouped by the
60 +effective user group under which each process runs.
61
62 ## Charts
63
@@ -82,7 +109,7 @@ The above are reported:
109 `apps.plugin` is a complex piece of software and has a lot of work to do
110 We are proud that `apps.plugin` is a lot faster compared to any other similar tool,
111 while collecting a lot more information for the processes, however the fact is that
85 -this plugin requires more CPU resources than the `netdata` daemon itself.
112 +this plugin may require more CPU resources than the `netdata` daemon itself.
113
114 Under Linux, for each process running, `apps.plugin` reads several `/proc` files
115 per process. Doing this work per-second, especially on hosts with several thousands
@@ -103,7 +130,7 @@ its CPU resources will be cut in half, and data collection will be once every 2
130
131 ## Configuration
132
106 -The configuration file is `/etc/netdata/apps_groups.conf`. To edit it on your system, run `/etc/netdata/edit-config apps_groups.conf`.
133 +The configuration file is `/etc/netdata/apps_groups.conf`. You can edit this file using our [`edit-config`](docs/netdata-agent/configuration/README.md) script.
134
135 The configuration file works accepts multiple lines, each having this format:
136
@@ -381,14 +408,14 @@ the process tree of `sshd`, **including the exited children**.
408 > `apps.plugin` does not use these mechanisms. The process grouping made by `apps.plugin` works
409 > on any Linux, `systemd` based or not.
410
384 -#### a more technical description of how Netdata works
411 +#### a more technical description of how apps.plugin works
412
386 -Netdata reads `/proc/<pid>/stat` for all processes, once per second and extracts `utime` and
413 +Apps.plugin reads `/proc/<pid>/stat` for all processes, once per second and extracts `utime` and
414 `stime` (user and system cpu utilization), much like all the console tools do.
415
389 -But it also extracts `cutime` and `cstime` that account the user and system time of the exit children of each process.
390 -By keeping a map in memory of the whole process tree, it is capable of assigning the right time to every process, taking
391 -into account all its exited children.
416 +But it also extracts `cutime` and `cstime` that account the user and system time of the exit
417 +children of each process. By keeping a map in memory of the whole process tree, it is capable of
418 +assigning the right time to every process, taking into account all its exited children.
419
420 It is tricky, since a process may be running for 1 hour and once it exits, its parent should not
421 receive the whole 1 hour of cpu time in just 1 second - you have to subtract the cpu time that has
@@ -397,6 +424,4 @@ been reported for it prior to this iteration.
424 It is even trickier, because walking through the entire process tree takes some time itself. So,
425 if you sum the CPU utilization of all processes, you might have more CPU time than the reported
426 total cpu time of the system. Netdata solves this, by adapting the per process cpu utilization to
400 -the total of the system. [Netdata adds charts that document this normalization](https://london.my-netdata.io/default.html#menu_netdata_submenu_apps_plugin).
401 -
402 -
427 +the total of the system. [Apps.plugin adds charts that document this normalization](https://london.my-netdata.io/default.html#menu_netdata_submenu_apps_plugin).
src/collectors/apps.plugin/apps_aggregations.c new
+237
@@ -0,0 +1,237 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +// update statistics on the targets
7 +
8 +static size_t zero_all_targets(struct target *root) {
9 + struct target *w;
10 + size_t count = 0;
11 +
12 + for (w = root; w ; w = w->next) {
13 + count++;
14 +
15 + for(size_t f = 0; f < PDF_MAX ;f++)
16 + w->values[f] = 0;
17 +
18 + w->uptime_min = 0;
19 + w->uptime_max = 0;
20 +
21 +#if (PROCESSES_HAVE_FDS == 1)
22 + // zero file counters
23 + if(w->target_fds) {
24 + memset(w->target_fds, 0, sizeof(int) * w->target_fds_size);
25 + w->openfds.files = 0;
26 + w->openfds.pipes = 0;
27 + w->openfds.sockets = 0;
28 + w->openfds.inotifies = 0;
29 + w->openfds.eventfds = 0;
30 + w->openfds.timerfds = 0;
31 + w->openfds.signalfds = 0;
32 + w->openfds.eventpolls = 0;
33 + w->openfds.other = 0;
34 +
35 + w->max_open_files_percent = 0.0;
36 + }
37 +#endif
38 +
39 + if(unlikely(w->root_pid)) {
40 + struct pid_on_target *pid_on_target = w->root_pid;
41 +
42 + while(pid_on_target) {
43 + struct pid_on_target *pid_on_target_to_free = pid_on_target;
44 + pid_on_target = pid_on_target->next;
45 + freez(pid_on_target_to_free);
46 + }
47 +
48 + w->root_pid = NULL;
49 + }
50 + }
51 +
52 + return count;
53 +}
54 +
55 +static inline void aggregate_pid_on_target(struct target *w, struct pid_stat *p, struct target *o __maybe_unused) {
56 + if(unlikely(!p->updated)) {
57 + // the process is not running
58 + return;
59 + }
60 +
61 + if(unlikely(!w)) {
62 + netdata_log_error("pid %d %s was left without a target!", p->pid, pid_stat_comm(p));
63 + return;
64 + }
65 +
66 +#if (PROCESSES_HAVE_FDS == 1) && (PROCESSES_HAVE_PID_LIMITS == 1)
67 + if(p->openfds_limits_percent > w->max_open_files_percent)
68 + w->max_open_files_percent = p->openfds_limits_percent;
69 +#endif
70 +
71 + for(size_t f = 0; f < PDF_MAX ;f++)
72 + w->values[f] += p->values[f];
73 +
74 + if(!w->uptime_min || p->values[PDF_UPTIME] < w->uptime_min) w->uptime_min = p->values[PDF_UPTIME];
75 + if(!w->uptime_max || w->uptime_max < p->values[PDF_UPTIME]) w->uptime_max = p->values[PDF_UPTIME];
76 +
77 + if(unlikely(debug_enabled || w->debug_enabled)) {
78 + struct pid_on_target *pid_on_target = mallocz(sizeof(struct pid_on_target));
79 + pid_on_target->pid = p->pid;
80 + pid_on_target->next = w->root_pid;
81 + w->root_pid = pid_on_target;
82 + }
83 +}
84 +
85 +static inline void cleanup_exited_pids(void) {
86 + struct pid_stat *p = NULL;
87 +
88 + for(p = root_of_pids(); p ;) {
89 + if(!p->updated && (!p->keep || p->keeploops > 0)) {
90 + if(unlikely(debug_enabled && (p->keep || p->keeploops)))
91 + debug_log(" > CLEANUP cannot keep exited process %d (%s) anymore - removing it.", p->pid, pid_stat_comm(p));
92 +
93 +#if (PROCESSES_HAVE_FDS == 1)
94 + for(size_t c = 0; c < p->fds_size; c++)
95 + if(p->fds[c].fd > 0) {
96 + file_descriptor_not_used(p->fds[c].fd);
97 + clear_pid_fd(&p->fds[c]);
98 + }
99 +#endif
100 +
101 + const pid_t r = p->pid;
102 + p = p->next;
103 + del_pid_entry(r);
104 + }
105 + else {
106 + if(unlikely(p->keep)) p->keeploops++;
107 + p->keep = false;
108 + p = p->next;
109 + }
110 + }
111 +}
112 +
113 +static struct target *get_app_group_target_for_pid(struct pid_stat *p) {
114 + targets_assignment_counter++;
115 +
116 + for(struct target *w = apps_groups_root_target; w ; w = w->next) {
117 + if(w->type != TARGET_TYPE_APP_GROUP) continue;
118 +
119 + // find it - 4 cases:
120 + // 1. the target is not a pattern
121 + // 2. the target has the prefix
122 + // 3. the target has the suffix
123 + // 4. the target is something inside cmdline
124 +
125 + if(unlikely(( (!w->starts_with && !w->ends_with && w->compare == p->comm)
126 + || (w->starts_with && !w->ends_with && string_starts_with_string(p->comm, w->compare))
127 + || (!w->starts_with && w->ends_with && string_ends_with_string(p->comm, w->compare))
128 + || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(pid_stat_cmdline(p), string2str(w->compare)))
129 + ))) {
130 +
131 + p->matched_by_config = true;
132 + if(w->target) return w->target;
133 + else return w;
134 + }
135 + }
136 +
137 + return NULL;
138 +}
139 +
140 +static void assign_a_target_to_all_processes(void) {
141 + // assign targets from app_groups.conf
142 + for(struct pid_stat *p = root_of_pids(); p ; p = p->next) {
143 + if(!p->target)
144 + p->target = get_app_group_target_for_pid(p);
145 + }
146 +
147 + // assign targets from their parents, if they have
148 + for(struct pid_stat *p = root_of_pids(); p ; p = p->next) {
149 + if(!p->target) {
150 + for(struct pid_stat *pp = p->parent ; pp ; pp = pp->parent) {
151 + if(pp->target) {
152 + if(pp->matched_by_config) {
153 + // we are only interested about app_groups.conf matches
154 + p->target = pp->target;
155 + }
156 + break;
157 + }
158 + }
159 +
160 + if(!p->target) {
161 + // there is no target, get it from the tree
162 + p->target = get_tree_target(p);
163 + }
164 + }
165 +
166 + fatal_assert(p->target != NULL);
167 + }
168 +}
169 +
170 +void aggregate_processes_to_targets(void) {
171 + assign_a_target_to_all_processes();
172 + apps_groups_targets_count = zero_all_targets(apps_groups_root_target);
173 +
174 +#if (PROCESSES_HAVE_UID == 1)
175 + zero_all_targets(users_root_target);
176 +#endif
177 +#if (PROCESSES_HAVE_GID == 1)
178 + zero_all_targets(groups_root_target);
179 +#endif
180 +
181 + // this has to be done, before the cleanup
182 + struct target *w = NULL, *o = NULL;
183 +
184 + // concentrate everything on the targets
185 + for(struct pid_stat *p = root_of_pids(); p ; p = p->next) {
186 +
187 + // --------------------------------------------------------------------
188 + // apps_groups and tree target
189 +
190 + aggregate_pid_on_target(p->target, p, NULL);
191 +
192 +
193 + // --------------------------------------------------------------------
194 + // user target
195 +
196 +#if (PROCESSES_HAVE_UID == 1)
197 + o = p->uid_target;
198 + if(likely(p->uid_target && p->uid_target->uid == p->uid))
199 + w = p->uid_target;
200 + else {
201 + if(unlikely(debug_enabled && p->uid_target))
202 + debug_log("pid %d (%s) switched user from %u (%s) to %u.", p->pid, pid_stat_comm(p), p->uid_target->uid, p->uid_target->name, p->uid);
203 +
204 + w = p->uid_target = get_uid_target(p->uid);
205 + }
206 +
207 + aggregate_pid_on_target(w, p, o);
208 +#endif
209 +
210 + // --------------------------------------------------------------------
211 + // user group target
212 +
213 +#if (PROCESSES_HAVE_GID == 1)
214 + o = p->gid_target;
215 + if(likely(p->gid_target && p->gid_target->gid == p->gid))
216 + w = p->gid_target;
217 + else {
218 + if(unlikely(debug_enabled && p->gid_target))
219 + debug_log("pid %d (%s) switched group from %u (%s) to %u.", p->pid, pid_stat_comm(p), p->gid_target->gid, p->gid_target->name, p->gid);
220 +
221 + w = p->gid_target = get_gid_target(p->gid);
222 + }
223 +
224 + aggregate_pid_on_target(w, p, o);
225 +#endif
226 +
227 + // --------------------------------------------------------------------
228 + // aggregate all file descriptors
229 +
230 +#if (PROCESSES_HAVE_FDS == 1)
231 + if(enable_file_charts)
232 + aggregate_pid_fds_on_targets(p);
233 +#endif
234 + }
235 +
236 + cleanup_exited_pids();
237 +}
src/collectors/apps.plugin/apps_functions.c
+278 -86
@@ -24,24 +24,35 @@ static void apps_plugin_function_processes_help(const char *transaction) {
24 " category:NAME\n"
25 " Shows only processes that are assigned the category `NAME` in apps_groups.conf\n"
26 "\n"
27 + " parent:NAME\n"
28 + " Shows only processes that are aggregated under parent `NAME`\n"
29 + "\n"
30 +#if (PROCESSES_HAVE_UID == 1)
31 " user:NAME\n"
32 " Shows only processes that are running as user name `NAME`.\n"
33 "\n"
34 +#endif
35 +#if (PROCESSES_HAVE_GID == 1)
36 " group:NAME\n"
37 " Shows only processes that are running as group name `NAME`.\n"
38 "\n"
39 +#endif
40 " process:NAME\n"
41 " Shows only processes that their Command is `NAME` or their parent's Command is `NAME`.\n"
42 "\n"
43 " pid:NUMBER\n"
44 " Shows only processes that their PID is `NUMBER` or their parent's PID is `NUMBER`\n"
45 "\n"
46 +#if (PROCESSES_HAVE_UID == 1)
47 " uid:NUMBER\n"
48 " Shows only processes that their UID is `NUMBER`\n"
49 "\n"
50 +#endif
51 +#if (PROCESSES_HAVE_GID == 1)
52 " gid:NUMBER\n"
53 " Shows only processes that their GID is `NUMBER`\n"
54 "\n"
55 +#endif
56 "Filters can be combined. Each filter can be given only one time.\n"
57 );
58
@@ -72,21 +83,20 @@ void function_processes(const char *transaction, char *function,
83 struct pid_stat *p;
84
85 bool show_cmdline = http_access_user_has_enough_access_level_for_endpoint(
75 - access,
76 - HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA |
77 - HTTP_ACCESS_VIEW_AGENT_CONFIG) || enable_function_cmdline;
86 + access, HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA | HTTP_ACCESS_VIEW_AGENT_CONFIG) || enable_function_cmdline;
87
88 char *words[PLUGINSD_MAX_WORDS] = { NULL };
89 size_t num_words = quoted_strings_splitter_pluginsd(function, words, PLUGINSD_MAX_WORDS);
90
82 - struct target *category = NULL, *user = NULL, *group = NULL;
91 + struct target *category = NULL, *user = NULL, *group = NULL; (void)category; (void)user; (void)group;
92 const char *process_name = NULL;
93 pid_t pid = 0;
85 - uid_t uid = 0;
86 - gid_t gid = 0;
94 + uid_t uid = 0; (void)uid;
95 + gid_t gid = 0; (void)gid;
96 bool info = false;
97
98 bool filter_pid = false, filter_uid = false, filter_gid = false;
99 + (void)filter_uid; (void)filter_gid;
100
101 for(int i = 1; i < PLUGINSD_MAX_WORDS ;i++) {
102 const char *keyword = get_word(words, num_words, i);
@@ -100,6 +110,7 @@ void function_processes(const char *transaction, char *function,
110 return;
111 }
112 }
113 +#if (PROCESSES_HAVE_UID == 1)
114 else if(!user && strncmp(keyword, PROCESS_FILTER_USER, strlen(PROCESS_FILTER_USER)) == 0) {
115 user = find_target_by_name(users_root_target, &keyword[strlen(PROCESS_FILTER_USER)]);
116 if(!user) {
@@ -108,6 +119,8 @@ void function_processes(const char *transaction, char *function,
119 return;
120 }
121 }
122 +#endif
123 +#if (PROCESSES_HAVE_GID == 1)
124 else if(strncmp(keyword, PROCESS_FILTER_GROUP, strlen(PROCESS_FILTER_GROUP)) == 0) {
125 group = find_target_by_name(groups_root_target, &keyword[strlen(PROCESS_FILTER_GROUP)]);
126 if(!group) {
@@ -116,6 +129,7 @@ void function_processes(const char *transaction, char *function,
129 return;
130 }
131 }
132 +#endif
133 else if(!process_name && strncmp(keyword, PROCESS_FILTER_PROCESS, strlen(PROCESS_FILTER_PROCESS)) == 0) {
134 process_name = &keyword[strlen(PROCESS_FILTER_PROCESS)];
135 }
@@ -123,14 +137,18 @@ void function_processes(const char *transaction, char *function,
137 pid = str2i(&keyword[strlen(PROCESS_FILTER_PID)]);
138 filter_pid = true;
139 }
140 +#if (PROCESSES_HAVE_UID == 1)
141 else if(!uid && strncmp(keyword, PROCESS_FILTER_UID, strlen(PROCESS_FILTER_UID)) == 0) {
142 uid = str2i(&keyword[strlen(PROCESS_FILTER_UID)]);
143 filter_uid = true;
144 }
145 +#endif
146 +#if (PROCESSES_HAVE_GID == 1)
147 else if(!gid && strncmp(keyword, PROCESS_FILTER_GID, strlen(PROCESS_FILTER_GID)) == 0) {
148 gid = str2i(&keyword[strlen(PROCESS_FILTER_GID)]);
149 filter_gid = true;
150 }
151 +#endif
152 else if(strcmp(keyword, "help") == 0) {
153 apps_plugin_function_processes_help(transaction);
154 return;
@@ -140,10 +158,6 @@ void function_processes(const char *transaction, char *function,
158 }
159 }
160
143 - unsigned int cpu_divisor = time_factor * RATES_DETAIL / 100;
144 - unsigned int memory_divisor = 1024;
145 - unsigned int io_divisor = 1024 * RATES_DETAIL;
146 -
161 BUFFER *wb = buffer_create(4096, NULL);
162 buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
163 buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
@@ -156,38 +170,71 @@ void function_processes(const char *transaction, char *function,
170 if(info)
171 goto close_and_send;
172
173 + uint64_t cpu_divisor = NSEC_PER_SEC / 100;
174 + unsigned int memory_divisor = 1024 * 1024;
175 + unsigned int io_divisor = 1024 * RATES_DETAIL;
176 +
177 + uint64_t total_memory_bytes = OS_FUNCTION(apps_os_get_total_memory)();
178 +
179 NETDATA_DOUBLE
160 - UserCPU_max = 0.0
180 + UserCPU_max = 0.0
181 , SysCPU_max = 0.0
182 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
183 , GuestCPU_max = 0.0
184 +#endif
185 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
186 , CUserCPU_max = 0.0
187 , CSysCPU_max = 0.0
188 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
189 , CGuestCPU_max = 0.0
190 +#endif
191 +#endif
192 , CPU_max = 0.0
193 , VMSize_max = 0.0
194 , RSS_max = 0.0
195 +#if (PROCESSES_HAVE_VMSHARED == 1)
196 , Shared_max = 0.0
197 +#endif
198 , Swap_max = 0.0
199 , Memory_max = 0.0
200 +#if (PROCESSES_HAVE_FDS == 1) && (PROCESSES_HAVE_PID_LIMITS == 1)
201 , FDsLimitPercent_max = 0.0
202 +#endif
203 ;
204
205 unsigned long long
206 Processes_max = 0
207 , Threads_max = 0
208 +#if (PROCESSES_HAVE_VOLCTX == 1)
209 , VoluntaryCtxtSwitches_max = 0
210 +#endif
211 +#if (PROCESSES_HAVE_NVOLCTX == 1)
212 , NonVoluntaryCtxtSwitches_max = 0
213 +#endif
214 , Uptime_max = 0
215 , MinFlt_max = 0
182 - , CMinFlt_max = 0
183 - , TMinFlt_max = 0
216 +#if (PROCESSES_HAVE_MAJFLT == 1)
217 , MajFlt_max = 0
218 +#endif
219 +#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
220 + , CMinFlt_max = 0
221 , CMajFlt_max = 0
222 + , TMinFlt_max = 0
223 , TMajFlt_max = 0
224 +#endif
225 +#if (PROCESSES_HAVE_LOGICAL_IO == 1)
226 + , LReads_max = 0
227 + , LWrites_max = 0
228 +#endif
229 +#if (PROCESSES_HAVE_PHYSICAL_IO == 1)
230 , PReads_max = 0
231 , PWrites_max = 0
189 - , RCalls_max = 0
190 - , WCalls_max = 0
232 +#endif
233 +#if (PROCESSES_HAVE_IO_CALLS == 1)
234 + , ROps_max = 0
235 + , WOps_max = 0
236 +#endif
237 +#if (PROCESSES_HAVE_FDS == 1)
238 , Files_max = 0
239 , Pipes_max = 0
240 , Sockets_max = 0
@@ -198,40 +245,47 @@ void function_processes(const char *transaction, char *function,
245 , EvPollFDs_max = 0
246 , OtherFDs_max = 0
247 , FDs_max = 0
248 +#endif
249 +#if (PROCESSES_HAVE_HANDLES == 1)
250 + , Handles_max = 0
251 +#endif
252 ;
253
203 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
204 - unsigned long long
205 - LReads_max = 0
206 - , LWrites_max = 0
207 - ;
208 -#endif // !__FreeBSD__ !__APPLE_
254 + netdata_mutex_lock(&apps_and_stdout_mutex);
255
256 int rows= 0;
211 - for(p = root_of_pids; p ; p = p->next) {
257 + for(p = root_of_pids(); p ; p = p->next) {
258 if(!p->updated)
259 continue;
260
261 if(category && p->target != category)
262 continue;
263
218 - if(user && p->user_target != user)
264 +#if (PROCESSES_HAVE_UID == 1)
265 + if(user && p->uid_target != user)
266 continue;
267 +#endif
268
221 - if(group && p->group_target != group)
269 +#if (PROCESSES_HAVE_GID == 1)
270 + if(group && p->gid_target != group)
271 continue;
272 +#endif
273
224 - if(process_name && ((strcmp(p->comm, process_name) != 0 && !p->parent) || (p->parent && strcmp(p->comm, process_name) != 0 && strcmp(p->parent->comm, process_name) != 0)))
274 + if(process_name && ((strcmp(pid_stat_comm(p), process_name) != 0 && !p->parent) || (p->parent && strcmp(pid_stat_comm(p), process_name) != 0 && strcmp(pid_stat_comm(p->parent), process_name) != 0)))
275 continue;
276
277 if(filter_pid && p->pid != pid && p->ppid != pid)
278 continue;
279
280 +#if (PROCESSES_HAVE_UID == 1)
281 if(filter_uid && p->uid != uid)
282 continue;
283 +#endif
284
285 +#if (PROCESSES_HAVE_GID == 1)
286 if(filter_gid && p->gid != gid)
287 continue;
288 +#endif
289
290 rows++;
291
@@ -244,80 +298,126 @@ void function_processes(const char *transaction, char *function,
298 buffer_json_add_array_item_uint64(wb, p->pid);
299
300 // cmd
247 - buffer_json_add_array_item_string(wb, p->comm);
301 + buffer_json_add_array_item_string(wb, string2str(p->comm));
302 +
303 +#if (PROCESSES_HAVE_COMM_AND_NAME == 1)
304 + // name
305 + buffer_json_add_array_item_string(wb, string2str(p->name ? p->name : p->comm));
306 +#endif
307
308 // cmdline
309 if (show_cmdline) {
251 - buffer_json_add_array_item_string(wb, (p->cmdline && *p->cmdline) ? p->cmdline : p->comm);
310 + buffer_json_add_array_item_string(wb, (string_strlen(p->cmdline)) ? pid_stat_cmdline(p) : pid_stat_comm(p));
311 }
312
313 // ppid
314 buffer_json_add_array_item_uint64(wb, p->ppid);
315
316 // category
258 - buffer_json_add_array_item_string(wb, p->target ? p->target->name : "-");
317 + buffer_json_add_array_item_string(wb, p->target ? string2str(p->target->name) : "-");
318
319 +#if (PROCESSES_HAVE_UID == 1)
320 // user
261 - buffer_json_add_array_item_string(wb, p->user_target ? p->user_target->name : "-");
321 + buffer_json_add_array_item_string(wb, p->uid_target ? string2str(p->uid_target->name) : "-");
322
323 // uid
324 buffer_json_add_array_item_uint64(wb, p->uid);
325 +#endif
326
327 +#if (PROCESSES_HAVE_GID == 1)
328 // group
267 - buffer_json_add_array_item_string(wb, p->group_target ? p->group_target->name : "-");
329 + buffer_json_add_array_item_string(wb, p->gid_target ? string2str(p->gid_target->name) : "-");
330
331 // gid
332 buffer_json_add_array_item_uint64(wb, p->gid);
333 +#endif
334
335 // CPU utilization %
273 - add_value_field_ndd_with_max(wb, CPU, (NETDATA_DOUBLE)(p->utime + p->stime + p->gtime + p->cutime + p->cstime + p->cgtime) / cpu_divisor);
274 - add_value_field_ndd_with_max(wb, UserCPU, (NETDATA_DOUBLE)(p->utime) / cpu_divisor);
275 - add_value_field_ndd_with_max(wb, SysCPU, (NETDATA_DOUBLE)(p->stime) / cpu_divisor);
276 - add_value_field_ndd_with_max(wb, GuestCPU, (NETDATA_DOUBLE)(p->gtime) / cpu_divisor);
277 - add_value_field_ndd_with_max(wb, CUserCPU, (NETDATA_DOUBLE)(p->cutime) / cpu_divisor);
278 - add_value_field_ndd_with_max(wb, CSysCPU, (NETDATA_DOUBLE)(p->cstime) / cpu_divisor);
279 - add_value_field_ndd_with_max(wb, CGuestCPU, (NETDATA_DOUBLE)(p->cgtime) / cpu_divisor);
336 + kernel_uint_t total_cpu = p->values[PDF_UTIME] + p->values[PDF_STIME];
337
281 - add_value_field_llu_with_max(wb, VoluntaryCtxtSwitches, p->status_voluntary_ctxt_switches / RATES_DETAIL);
282 - add_value_field_llu_with_max(wb, NonVoluntaryCtxtSwitches, p->status_nonvoluntary_ctxt_switches / RATES_DETAIL);
338 +#if (PROCESSES_HAVE_CPU_GUEST_TIME)
339 + total_cpu += p->values[PDF_GTIME];
340 +#endif
341 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME)
342 + total_cpu += p->values[PDF_CUTIME] + p->values[PDF_CSTIME];
343 +#if (PROCESSES_HAVE_CPU_GUEST_TIME)
344 + total_cpu += p->values[PDF_CGTIME];
345 +#endif
346 +#endif
347 + add_value_field_ndd_with_max(wb, CPU, (NETDATA_DOUBLE)(total_cpu) / cpu_divisor);
348 + add_value_field_ndd_with_max(wb, UserCPU, (NETDATA_DOUBLE)(p->values[PDF_UTIME]) / cpu_divisor);
349 + add_value_field_ndd_with_max(wb, SysCPU, (NETDATA_DOUBLE)(p->values[PDF_STIME]) / cpu_divisor);
350 +#if (PROCESSES_HAVE_CPU_GUEST_TIME)
351 + add_value_field_ndd_with_max(wb, GuestCPU, (NETDATA_DOUBLE)(p->values[PDF_GTIME]) / cpu_divisor);
352 +#endif
353 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME)
354 + add_value_field_ndd_with_max(wb, CUserCPU, (NETDATA_DOUBLE)(p->values[PDF_CUTIME]) / cpu_divisor);
355 + add_value_field_ndd_with_max(wb, CSysCPU, (NETDATA_DOUBLE)(p->values[PDF_CSTIME]) / cpu_divisor);
356 +#if (PROCESSES_HAVE_CPU_GUEST_TIME)
357 + add_value_field_ndd_with_max(wb, CGuestCPU, (NETDATA_DOUBLE)(p->values[PDF_CGTIME]) / cpu_divisor);
358 +#endif
359 +#endif
360 +
361 +#if (PROCESSES_HAVE_VOLCTX == 1)
362 + add_value_field_llu_with_max(wb, VoluntaryCtxtSwitches, p->values[PDF_VOLCTX] / RATES_DETAIL);
363 +#endif
364 +#if (PROCESSES_HAVE_NVOLCTX == 1)
365 + add_value_field_llu_with_max(wb, NonVoluntaryCtxtSwitches, p->values[PDF_NVOLCTX] / RATES_DETAIL);
366 +#endif
367
368 // memory MiB
285 - if(MemTotal)
286 - add_value_field_ndd_with_max(wb, Memory, (NETDATA_DOUBLE)p->status_vmrss * 100.0 / (NETDATA_DOUBLE)MemTotal);
369 + if(total_memory_bytes)
370 + add_value_field_ndd_with_max(wb, Memory, (NETDATA_DOUBLE)p->values[PDF_VMRSS] * 100.0 / (NETDATA_DOUBLE)total_memory_bytes);
371 +
372 + add_value_field_ndd_with_max(wb, RSS, (NETDATA_DOUBLE)p->values[PDF_VMRSS] / memory_divisor);
373
288 - add_value_field_ndd_with_max(wb, RSS, (NETDATA_DOUBLE)p->status_vmrss / memory_divisor);
289 - add_value_field_ndd_with_max(wb, Shared, (NETDATA_DOUBLE)p->status_vmshared / memory_divisor);
290 -#if !defined(__APPLE__)
291 - add_value_field_ndd_with_max(wb, VMSize, (NETDATA_DOUBLE)p->status_vmsize / memory_divisor);
374 +#if (PROCESSES_HAVE_VMSHARED == 1)
375 + add_value_field_ndd_with_max(wb, Shared, (NETDATA_DOUBLE)p->values[PDF_VMSHARED] / memory_divisor);
376 #endif
293 - add_value_field_ndd_with_max(wb, Swap, (NETDATA_DOUBLE)p->status_vmswap / memory_divisor);
377
378 + add_value_field_ndd_with_max(wb, VMSize, (NETDATA_DOUBLE)p->values[PDF_VMSIZE] / memory_divisor);
379 +#if (PROCESSES_HAVE_VMSWAP == 1)
380 + add_value_field_ndd_with_max(wb, Swap, (NETDATA_DOUBLE)p->values[PDF_VMSWAP] / memory_divisor);
381 +#endif
382 +
383 +#if (PROCESSES_HAVE_PHYSICAL_IO == 1)
384 // Physical I/O
296 - add_value_field_llu_with_max(wb, PReads, p->io_storage_bytes_read / io_divisor);
297 - add_value_field_llu_with_max(wb, PWrites, p->io_storage_bytes_written / io_divisor);
385 + add_value_field_llu_with_max(wb, PReads, p->values[PDF_PREAD] / io_divisor);
386 + add_value_field_llu_with_max(wb, PWrites, p->values[PDF_PWRITE] / io_divisor);
387 +#endif
388
389 +#if (PROCESSES_HAVE_LOGICAL_IO == 1)
390 // Logical I/O
300 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
301 - add_value_field_llu_with_max(wb, LReads, p->io_logical_bytes_read / io_divisor);
302 - add_value_field_llu_with_max(wb, LWrites, p->io_logical_bytes_written / io_divisor);
391 + add_value_field_llu_with_max(wb, LReads, p->values[PDF_LREAD] / io_divisor);
392 + add_value_field_llu_with_max(wb, LWrites, p->values[PDF_LWRITE] / io_divisor);
393 #endif
394
395 +#if (PROCESSES_HAVE_IO_CALLS == 1)
396 // I/O calls
306 - add_value_field_llu_with_max(wb, RCalls, p->io_read_calls / RATES_DETAIL);
307 - add_value_field_llu_with_max(wb, WCalls, p->io_write_calls / RATES_DETAIL);
397 + add_value_field_llu_with_max(wb, ROps, p->values[PDF_OREAD] / RATES_DETAIL);
398 + add_value_field_llu_with_max(wb, WOps, p->values[PDF_OWRITE] / RATES_DETAIL);
399 +#endif
400
401 // minor page faults
310 - add_value_field_llu_with_max(wb, MinFlt, p->minflt / RATES_DETAIL);
311 - add_value_field_llu_with_max(wb, CMinFlt, p->cminflt / RATES_DETAIL);
312 - add_value_field_llu_with_max(wb, TMinFlt, (p->minflt + p->cminflt) / RATES_DETAIL);
402 + add_value_field_llu_with_max(wb, MinFlt, p->values[PDF_MINFLT] / RATES_DETAIL);
403
404 +#if (PROCESSES_HAVE_MAJFLT == 1)
405 // major page faults
315 - add_value_field_llu_with_max(wb, MajFlt, p->majflt / RATES_DETAIL);
316 - add_value_field_llu_with_max(wb, CMajFlt, p->cmajflt / RATES_DETAIL);
317 - add_value_field_llu_with_max(wb, TMajFlt, (p->majflt + p->cmajflt) / RATES_DETAIL);
406 + add_value_field_llu_with_max(wb, MajFlt, p->values[PDF_MAJFLT] / RATES_DETAIL);
407 +#endif
408 +
409 +#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
410 + add_value_field_llu_with_max(wb, CMinFlt, p->values[PDF_CMINFLT] / RATES_DETAIL);
411 + add_value_field_llu_with_max(wb, CMajFlt, p->values[PDF_CMAJFLT] / RATES_DETAIL);
412 + add_value_field_llu_with_max(wb, TMinFlt, (p->values[PDF_MINFLT] + p->values[PDF_CMINFLT]) / RATES_DETAIL);
413 + add_value_field_llu_with_max(wb, TMajFlt, (p->values[PDF_MAJFLT] + p->values[PDF_CMAJFLT]) / RATES_DETAIL);
414 +#endif
415
416 +#if (PROCESSES_HAVE_FDS == 1)
417 // open file descriptors
418 +#if (PROCESSES_HAVE_PID_LIMITS == 1)
419 add_value_field_ndd_with_max(wb, FDsLimitPercent, p->openfds_limits_percent);
420 +#endif
421 add_value_field_llu_with_max(wb, FDs, pid_openfds_sum(p));
422 add_value_field_llu_with_max(wb, Files, p->openfds.files);
423 add_value_field_llu_with_max(wb, Pipes, p->openfds.pipes);
@@ -328,12 +428,16 @@ void function_processes(const char *transaction, char *function,
428 add_value_field_llu_with_max(wb, SigFDs, p->openfds.signalfds);
429 add_value_field_llu_with_max(wb, EvPollFDs, p->openfds.eventpolls);
430 add_value_field_llu_with_max(wb, OtherFDs, p->openfds.other);
431 +#endif
432
433 +#if (PROCESSES_HAVE_HANDLES == 1)
434 + add_value_field_llu_with_max(wb, Handles, p->values[PDF_HANDLES]);
435 +#endif
436
437 // processes, threads, uptime
334 - add_value_field_llu_with_max(wb, Processes, p->children_count);
335 - add_value_field_llu_with_max(wb, Threads, p->num_threads);
336 - add_value_field_llu_with_max(wb, Uptime, p->uptime);
438 + add_value_field_llu_with_max(wb, Processes, p->values[PDF_PROCESSES]);
439 + add_value_field_llu_with_max(wb, Threads, p->values[PDF_THREADS]);
440 + add_value_field_llu_with_max(wb, Uptime, p->values[PDF_UPTIME]);
441
442 buffer_json_array_close(wb); // for each pid
443 }
@@ -360,6 +464,14 @@ void function_processes(const char *transaction, char *function,
464 RRDF_FIELD_FILTER_MULTISELECT,
465 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
466
467 +#if (PROCESSES_HAVE_COMM_AND_NAME == 1)
468 + buffer_rrdf_table_add_field(wb, field_id++, "Name", "Process Friendly Name", RRDF_FIELD_TYPE_STRING,
469 + RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
470 + RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
471 + RRDF_FIELD_FILTER_MULTISELECT,
472 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
473 +#endif
474 +
475 if (show_cmdline) {
476 buffer_rrdf_table_add_field(wb, field_id++, "CmdLine", "Command Line", RRDF_FIELD_TYPE_STRING,
477 RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0,
@@ -373,12 +485,15 @@ void function_processes(const char *transaction, char *function,
485 NAN, RRDF_FIELD_SORT_ASCENDING, "PID", RRDF_FIELD_SUMMARY_COUNT,
486 RRDF_FIELD_FILTER_MULTISELECT,
487 RRDF_FIELD_OPTS_NONE, NULL);
488 +
489 buffer_rrdf_table_add_field(wb, field_id++, "Category", "Category (apps_groups.conf)", RRDF_FIELD_TYPE_STRING,
490 RRDF_FIELD_VISUAL_VALUE,
491 RRDF_FIELD_TRANSFORM_NONE,
492 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
493 RRDF_FIELD_FILTER_MULTISELECT,
494 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
495 +
496 +#if (PROCESSES_HAVE_UID == 1)
497 buffer_rrdf_table_add_field(wb, field_id++, "User", "User Owner", RRDF_FIELD_TYPE_STRING,
498 RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
499 RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
@@ -389,6 +504,9 @@ void function_processes(const char *transaction, char *function,
504 RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
505 RRDF_FIELD_FILTER_MULTISELECT,
506 RRDF_FIELD_OPTS_NONE, NULL);
507 +#endif
508 +
509 +#if (PROCESSES_HAVE_GID == 1)
510 buffer_rrdf_table_add_field(wb, field_id++, "Group", "Group Owner", RRDF_FIELD_TYPE_STRING,
511 RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
512 RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
@@ -399,6 +517,7 @@ void function_processes(const char *transaction, char *function,
517 RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
518 RRDF_FIELD_FILTER_MULTISELECT,
519 RRDF_FIELD_OPTS_NONE, NULL);
520 +#endif
521
522 // CPU utilization
523 buffer_rrdf_table_add_field(wb, field_id++, "CPU", "Total CPU Time (100% = 1 core)",
@@ -416,11 +535,14 @@ void function_processes(const char *transaction, char *function,
535 RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", SysCPU_max,
536 RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
537 RRDF_FIELD_OPTS_NONE, NULL);
538 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
539 buffer_rrdf_table_add_field(wb, field_id++, "GuestCPU", "Guest CPU Time (100% = 1 core)",
540 RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
541 RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", GuestCPU_max,
542 RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
543 RRDF_FIELD_OPTS_NONE, NULL);
544 +#endif
545 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
546 buffer_rrdf_table_add_field(wb, field_id++, "CUserCPU", "Children User CPU Time (100% = 1 core)",
547 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
548 RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CUserCPU_max, RRDF_FIELD_SORT_DESCENDING, NULL,
@@ -431,26 +553,33 @@ void function_processes(const char *transaction, char *function,
553 RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CSysCPU_max, RRDF_FIELD_SORT_DESCENDING, NULL,
554 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
555 RRDF_FIELD_OPTS_NONE, NULL);
556 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
557 buffer_rrdf_table_add_field(wb, field_id++, "CGuestCPU", "Children Guest CPU Time (100% = 1 core)",
558 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
559 RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CGuestCPU_max, RRDF_FIELD_SORT_DESCENDING,
560 NULL,
561 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, RRDF_FIELD_OPTS_NONE, NULL);
562 +#endif
563 +#endif
564
565 +#if (PROCESSES_HAVE_VOLCTX == 1)
566 // CPU context switches
567 buffer_rrdf_table_add_field(wb, field_id++, "vCtxSwitch", "Voluntary Context Switches",
568 RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
569 RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "switches/s",
570 VoluntaryCtxtSwitches_max, RRDF_FIELD_SORT_DESCENDING, NULL,
571 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, RRDF_FIELD_OPTS_NONE, NULL);
572 +#endif
573 +#if (PROCESSES_HAVE_NVOLCTX == 1)
574 buffer_rrdf_table_add_field(wb, field_id++, "iCtxSwitch", "Involuntary Context Switches",
575 RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
576 RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "switches/s",
577 NonVoluntaryCtxtSwitches_max, RRDF_FIELD_SORT_DESCENDING, NULL,
578 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, RRDF_FIELD_OPTS_NONE, NULL);
579 +#endif
580
581 // memory
453 - if (MemTotal)
582 + if (total_memory_bytes)
583 buffer_rrdf_table_add_field(wb, field_id++, "Memory", "Memory Percentage", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
584 RRDF_FIELD_VISUAL_BAR,
585 RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", 100.0, RRDF_FIELD_SORT_DESCENDING, NULL,
@@ -463,25 +592,30 @@ void function_processes(const char *transaction, char *function,
592 2, "MiB", RSS_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
593 RRDF_FIELD_FILTER_RANGE,
594 RRDF_FIELD_OPTS_VISIBLE, NULL);
595 +#if (PROCESSES_HAVE_VMSHARED == 1)
596 buffer_rrdf_table_add_field(wb, field_id++, "Shared", "Shared Pages", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
597 RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
598 "MiB", Shared_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
599 RRDF_FIELD_FILTER_RANGE,
600 RRDF_FIELD_OPTS_VISIBLE, NULL);
471 -#if !defined(__APPLE__)
601 +#endif
602 +
603 buffer_rrdf_table_add_field(wb, field_id++, "Virtual", "Virtual Memory Size", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
604 RRDF_FIELD_VISUAL_BAR,
605 RRDF_FIELD_TRANSFORM_NUMBER, 2, "MiB", VMSize_max, RRDF_FIELD_SORT_DESCENDING, NULL,
606 RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
607 RRDF_FIELD_OPTS_VISIBLE, NULL);
477 -#endif
608 +
609 +#if (PROCESSES_HAVE_VMSWAP == 1)
610 buffer_rrdf_table_add_field(wb, field_id++, "Swap", "Swap Memory", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
611 RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
612 "MiB",
613 Swap_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
614 RRDF_FIELD_FILTER_RANGE,
615 RRDF_FIELD_OPTS_NONE, NULL);
616 +#endif
617
618 +#if (PROCESSES_HAVE_PHYSICAL_IO == 1)
619 // Physical I/O
620 buffer_rrdf_table_add_field(wb, field_id++, "PReads", "Physical I/O Reads", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
621 RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
@@ -493,33 +627,41 @@ void function_processes(const char *transaction, char *function,
627 RRDF_FIELD_TRANSFORM_NUMBER, 2, "KiB/s", PWrites_max, RRDF_FIELD_SORT_DESCENDING,
628 NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
629 RRDF_FIELD_OPTS_VISIBLE, NULL);
630 +#endif
631
632 +#if (PROCESSES_HAVE_LOGICAL_IO == 1)
633 +#if (PROCESSES_HAVE_PHYSICAL_IO == 1)
634 + RRDF_FIELD_OPTIONS logical_io_options = RRDF_FIELD_OPTS_NONE;
635 +#else
636 + RRDF_FIELD_OPTIONS logical_io_options = RRDF_FIELD_OPTS_VISIBLE;
637 +#endif
638 // Logical I/O
498 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
639 buffer_rrdf_table_add_field(wb, field_id++, "LReads", "Logical I/O Reads", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
640 RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
641 2, "KiB/s", LReads_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
642 RRDF_FIELD_FILTER_RANGE,
503 - RRDF_FIELD_OPTS_NONE, NULL);
643 + logical_io_options, NULL);
644 buffer_rrdf_table_add_field(wb, field_id++, "LWrites", "Logical I/O Writes", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
645 RRDF_FIELD_VISUAL_BAR,
646 RRDF_FIELD_TRANSFORM_NUMBER,
647 2, "KiB/s", LWrites_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
648 RRDF_FIELD_FILTER_RANGE,
509 - RRDF_FIELD_OPTS_NONE, NULL);
649 + logical_io_options, NULL);
650 #endif
651
652 +#if (PROCESSES_HAVE_IO_CALLS == 1)
653 // I/O calls
513 - buffer_rrdf_table_add_field(wb, field_id++, "RCalls", "I/O Read Calls", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
654 + buffer_rrdf_table_add_field(wb, field_id++, "ROps", "I/O Read Operations", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
655 RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
515 - "calls/s", RCalls_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
656 + "ops/s", ROps_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
657 RRDF_FIELD_FILTER_RANGE,
658 RRDF_FIELD_OPTS_NONE, NULL);
518 - buffer_rrdf_table_add_field(wb, field_id++, "WCalls", "I/O Write Calls", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
659 + buffer_rrdf_table_add_field(wb, field_id++, "WOps", "I/O Write Operations", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
660 RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
520 - "calls/s", WCalls_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
661 + "ops/s", WOps_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
662 RRDF_FIELD_FILTER_RANGE,
663 RRDF_FIELD_OPTS_NONE, NULL);
664 +#endif
665
666 // minor page faults
667 buffer_rrdf_table_add_field(wb, field_id++, "MinFlt", "Minor Page Faults/s", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
@@ -528,18 +670,8 @@ void function_processes(const char *transaction, char *function,
670 2, "pgflts/s", MinFlt_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
671 RRDF_FIELD_FILTER_RANGE,
672 RRDF_FIELD_OPTS_NONE, NULL);
531 - buffer_rrdf_table_add_field(wb, field_id++, "CMinFlt", "Children Minor Page Faults/s",
532 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
533 - RRDF_FIELD_VISUAL_BAR,
534 - RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", CMinFlt_max, RRDF_FIELD_SORT_DESCENDING,
535 - NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
536 - RRDF_FIELD_OPTS_NONE, NULL);
537 - buffer_rrdf_table_add_field(wb, field_id++, "TMinFlt", "Total Minor Page Faults/s",
538 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
539 - RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", TMinFlt_max, RRDF_FIELD_SORT_DESCENDING,
540 - NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
541 - RRDF_FIELD_OPTS_NONE, NULL);
673
674 +#if (PROCESSES_HAVE_MAJFLT == 1)
675 // major page faults
676 buffer_rrdf_table_add_field(wb, field_id++, "MajFlt", "Major Page Faults/s", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
677 RRDF_FIELD_VISUAL_BAR,
@@ -547,24 +679,42 @@ void function_processes(const char *transaction, char *function,
679 2, "pgflts/s", MajFlt_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
680 RRDF_FIELD_FILTER_RANGE,
681 RRDF_FIELD_OPTS_NONE, NULL);
682 +#endif
683 +
684 +#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
685 + buffer_rrdf_table_add_field(wb, field_id++, "CMinFlt", "Children Minor Page Faults/s",
686 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
687 + RRDF_FIELD_VISUAL_BAR,
688 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", CMinFlt_max, RRDF_FIELD_SORT_DESCENDING,
689 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
690 + RRDF_FIELD_OPTS_NONE, NULL);
691 buffer_rrdf_table_add_field(wb, field_id++, "CMajFlt", "Children Major Page Faults/s",
692 RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
693 RRDF_FIELD_VISUAL_BAR,
694 RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", CMajFlt_max, RRDF_FIELD_SORT_DESCENDING,
695 NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
696 RRDF_FIELD_OPTS_NONE, NULL);
697 + buffer_rrdf_table_add_field(wb, field_id++, "TMinFlt", "Total Minor Page Faults/s",
698 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
699 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", TMinFlt_max, RRDF_FIELD_SORT_DESCENDING,
700 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
701 + RRDF_FIELD_OPTS_NONE, NULL);
702 buffer_rrdf_table_add_field(wb, field_id++, "TMajFlt", "Total Major Page Faults/s",
703 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
704 RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", TMajFlt_max, RRDF_FIELD_SORT_DESCENDING,
705 NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
706 RRDF_FIELD_OPTS_NONE, NULL);
707 +#endif
708
709 +#if (PROCESSES_HAVE_FDS == 1)
710 // open file descriptors
711 +#if (PROCESSES_HAVE_PID_LIMITS == 1)
712 buffer_rrdf_table_add_field(wb, field_id++, "FDsLimitPercent", "Percentage of Open Descriptors vs Limits",
713 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
714 RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", FDsLimitPercent_max, RRDF_FIELD_SORT_DESCENDING, NULL,
715 RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
716 RRDF_FIELD_OPTS_NONE, NULL);
717 +#endif
718 buffer_rrdf_table_add_field(wb, field_id++, "FDs", "All Open File Descriptors",
719 RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
720 RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", FDs_max, RRDF_FIELD_SORT_DESCENDING, NULL,
@@ -617,6 +767,16 @@ void function_processes(const char *transaction, char *function,
767 RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", OtherFDs_max, RRDF_FIELD_SORT_DESCENDING,
768 NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
769 RRDF_FIELD_OPTS_NONE, NULL);
770 +#endif
771 +
772 +#if (PROCESSES_HAVE_HANDLES == 1)
773 + buffer_rrdf_table_add_field(wb, field_id++, "Handles", "Open Handles", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
774 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
775 + "handles",
776 + Handles_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
777 + RRDF_FIELD_FILTER_RANGE,
778 + RRDF_FIELD_OPTS_VISIBLE, NULL);
779 +#endif
780
781 // processes, threads, uptime
782 buffer_rrdf_table_add_field(wb, field_id++, "Processes", "Processes", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
@@ -650,27 +810,39 @@ void function_processes(const char *transaction, char *function,
810 {
811 buffer_json_add_array_item_string(wb, "UserCPU");
812 buffer_json_add_array_item_string(wb, "SysCPU");
813 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
814 buffer_json_add_array_item_string(wb, "GuestCPU");
815 +#endif
816 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
817 buffer_json_add_array_item_string(wb, "CUserCPU");
818 buffer_json_add_array_item_string(wb, "CSysCPU");
819 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
820 buffer_json_add_array_item_string(wb, "CGuestCPU");
821 +#endif
822 +#endif
823 }
824 buffer_json_array_close(wb);
825 }
826 buffer_json_object_close(wb);
827
828 +#if (PROCESSES_HAVE_VOLCTX == 1) || (PROCESSES_HAVE_NVOLCTX == 1)
829 buffer_json_member_add_object(wb, "CPUCtxSwitches");
830 {
831 buffer_json_member_add_string(wb, "name", "CPU Context Switches");
832 buffer_json_member_add_string(wb, "type", "stacked-bar");
833 buffer_json_member_add_array(wb, "columns");
834 {
835 +#if (PROCESSES_HAVE_VOLCTX == 1)
836 buffer_json_add_array_item_string(wb, "vCtxSwitch");
837 +#endif
838 +#if (PROCESSES_HAVE_NVOLCTX == 1)
839 buffer_json_add_array_item_string(wb, "iCtxSwitch");
840 +#endif
841 }
842 buffer_json_array_close(wb);
843 }
844 buffer_json_object_close(wb);
845 +#endif
846
847 // Memory chart
848 buffer_json_member_add_object(wb, "Memory");
@@ -688,7 +860,7 @@ void function_processes(const char *transaction, char *function,
860 }
861 buffer_json_object_close(wb);
862
691 - if(MemTotal) {
863 + if(total_memory_bytes) {
864 // Memory chart
865 buffer_json_member_add_object(wb, "MemoryPercent");
866 {
@@ -703,7 +875,7 @@ void function_processes(const char *transaction, char *function,
875 buffer_json_object_close(wb);
876 }
877
706 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
878 +#if (PROCESSES_HAVE_LOGICAL_IO == 1) || (PROCESSES_HAVE_PHYSICAL_IO == 1)
879 // I/O Reads chart
880 buffer_json_member_add_object(wb, "Reads");
881 {
@@ -711,8 +883,12 @@ void function_processes(const char *transaction, char *function,
883 buffer_json_member_add_string(wb, "type", "stacked-bar");
884 buffer_json_member_add_array(wb, "columns");
885 {
886 +#if (PROCESSES_HAVE_LOGICAL_IO == 1)
887 buffer_json_add_array_item_string(wb, "LReads");
888 +#endif
889 +#if (PROCESSES_HAVE_PHYSICAL_IO == 1)
890 buffer_json_add_array_item_string(wb, "PReads");
891 +#endif
892 }
893 buffer_json_array_close(wb);
894 }
@@ -725,13 +901,19 @@ void function_processes(const char *transaction, char *function,
901 buffer_json_member_add_string(wb, "type", "stacked-bar");
902 buffer_json_member_add_array(wb, "columns");
903 {
904 +#if (PROCESSES_HAVE_LOGICAL_IO == 1)
905 buffer_json_add_array_item_string(wb, "LWrites");
906 +#endif
907 +#if (PROCESSES_HAVE_PHYSICAL_IO == 1)
908 buffer_json_add_array_item_string(wb, "PWrites");
909 +#endif
910 }
911 buffer_json_array_close(wb);
912 }
913 buffer_json_object_close(wb);
914 +#endif
915
916 +#if (PROCESSES_HAVE_LOGICAL_IO == 1)
917 // Logical I/O chart
918 buffer_json_member_add_object(wb, "LogicalIO");
919 {
@@ -747,6 +929,7 @@ void function_processes(const char *transaction, char *function,
929 buffer_json_object_close(wb);
930 #endif
931
932 +#if (PROCESSES_HAVE_PHYSICAL_IO == 1)
933 // Physical I/O chart
934 buffer_json_member_add_object(wb, "PhysicalIO");
935 {
@@ -760,7 +943,9 @@ void function_processes(const char *transaction, char *function,
943 buffer_json_array_close(wb);
944 }
945 buffer_json_object_close(wb);
946 +#endif
947
948 +#if (PROCESSES_HAVE_IO_CALLS == 1)
949 // I/O Calls chart
950 buffer_json_member_add_object(wb, "IOCalls");
951 {
@@ -768,12 +953,13 @@ void function_processes(const char *transaction, char *function,
953 buffer_json_member_add_string(wb, "type", "stacked-bar");
954 buffer_json_member_add_array(wb, "columns");
955 {
771 - buffer_json_add_array_item_string(wb, "RCalls");
956 + buffer_json_add_array_item_string(wb, "ROps");
957 buffer_json_add_array_item_string(wb, "WCalls");
958 }
959 buffer_json_array_close(wb);
960 }
961 buffer_json_object_close(wb);
962 +#endif
963
964 // Minor Page Faults chart
965 buffer_json_member_add_object(wb, "MinFlt");
@@ -893,6 +1079,7 @@ void function_processes(const char *transaction, char *function,
1079 }
1080 buffer_json_object_close(wb);
1081
1082 +#if (PROCESSES_HAVE_UID == 1)
1083 // group by User
1084 buffer_json_member_add_object(wb, "User");
1085 {
@@ -905,7 +1092,9 @@ void function_processes(const char *transaction, char *function,
1092 buffer_json_array_close(wb);
1093 }
1094 buffer_json_object_close(wb);
1095 +#endif
1096
1097 +#if (PROCESSES_HAVE_GID == 1)
1098 // group by Group
1099 buffer_json_member_add_object(wb, "Group");
1100 {
@@ -918,9 +1107,12 @@ void function_processes(const char *transaction, char *function,
1107 buffer_json_array_close(wb);
1108 }
1109 buffer_json_object_close(wb);
1110 +#endif
1111 }
1112 buffer_json_object_close(wb); // group_by
1113
1114 + netdata_mutex_unlock(&apps_and_stdout_mutex);
1115 +
1116 close_and_send:
1117 buffer_json_member_add_time_t(wb, "expires", now_s + update_every);
1118 buffer_json_finalize(wb);
src/collectors/apps.plugin/apps_groups.conf
+54 -368
@@ -1,90 +1,41 @@
1 -#
2 -# apps.plugin process grouping
3 -#
4 -# The apps.plugin displays charts with information about the processes running.
5 -# This config allows grouping processes together, so that several processes
6 -# will be reported as one.
7 -#
8 -# Only groups in this file are reported. All other processes will be reported
9 -# as 'other'.
10 -#
11 -# For each process given, its whole process tree will be grouped, not just
12 -# the process matched. The plugin will include both parents and childs.
13 -#
14 -# The format is:
15 -#
16 -# group: process1 process2 process3 ...
17 -#
18 -# Each group can be given multiple times, to add more processes to it.
19 -#
20 -# The process names are the ones returned by:
21 -#
22 -# - ps -e or /proc/PID/stat
23 -# - in case of substring mode (see below): /proc/PID/cmdline
24 -#
25 -# To add process names with spaces, enclose them in quotes (single or double)
26 -# example: 'Plex Media Serv' "my other process".
27 -#
28 -# Note that spaces are not supported for process groups. Use a dash "-" instead.
29 -# example-process-group: process1 process2
30 -#
31 -# Wildcard support:
32 -# You can add an asterisk (*) at the beginning and/or the end of a process:
33 -#
34 -# *name suffix mode: will search for processes ending with 'name'
35 -# (/proc/PID/stat)
36 -#
37 -# name* prefix mode: will search for processes beginning with 'name'
38 -# (/proc/PID/stat)
39 -#
40 -# *name* substring mode: will search for 'name' in the whole command line
41 -# (/proc/PID/cmdline)
42 -#
43 -# If you enter even just one *name* (substring), apps.plugin will process
44 -# /proc/PID/cmdline for all processes, just once (when they are first seen).
45 -#
46 -# To add processes with single quotes, enclose them in double quotes
47 -# example: "process with this ' single quote"
48 -#
49 -# To add processes with double quotes, enclose them in single quotes:
50 -# example: 'process with this " double quote'
51 -#
52 -# If a group or process name starts with a -, the dimension will be hidden
53 -# (cpu chart only).
54 -#
55 -# If a process starts with a +, debugging will be enabled for it
56 -# (debugging produces a lot of output - do not enable it in production systems)
57 -#
58 -# You can add any number of groups you like. Only the ones found running will
59 -# affect the charts generated. However, producing charts with hundreds of
60 -# dimensions may slow down your web browser.
61 -#
62 -# The order of the entries in this list is important: the first that matches
63 -# a process is used, so put important ones at the top. Processes not matched
64 -# by any row, will inherit it from their parents or children.
65 -#
66 -# The order also controls the order of the dimensions on the generated charts
67 -# (although applications started after apps.plugin is started, will be appended
68 -# to the existing list of dimensions the netdata daemon maintains).
1 +##
2 +## apps.plugin process grouping
3 +##
4 +## Documentation at:
5 +## https://github.com/netdata/netdata/blob/master/src/collectors/apps.plugin/README.md
6 +##
7 +## The list of process managers can be configured here (uncomment and edit):
8
70 -# -----------------------------------------------------------------------------
71 -# NETDATA processes accounting
9 +## Linux
10 +#managers: init systemd containerd-shim dumb-init gnome-shell docker-init
11
73 -# netdata main process
74 -netdata: netdata
12 +## FreeBSD
13 +#managers: init
14 +
15 +## MacOS
16 +#managers: launchd
17 +
18 +## Windows
19 +#managers: System services wininit
20 +
21 +## -----------------------------------------------------------------------------
22 +## Processes of interest
23
76 -# netdata known plugins
77 -# plugins not defined here will be accumulated in netdata, above
78 -apps.plugin: apps.plugin
79 -freeipmi.plugin: freeipmi.plugin
80 -nfacct.plugin: nfacct.plugin
81 -cups.plugin: cups.plugin
82 -xenstat.plugin: xenstat.plugin
83 -perf.plugin: perf.plugin
24 +## NETDATA processes accounting
25 +netdata: netdata
26 +## netdata known plugins
27 +## plugins not defined here will be accumulated into netdata, above
28 +apps.plugin: *apps.plugin*
29 +freeipmi.plugin: *freeipmi.plugin*
30 +nfacct.plugin: *nfacct.plugin*
31 +cups.plugin: *cups.plugin*
32 +xenstat.plugin: *xenstat.plugin*
33 +perf.plugin: *perf.plugin*
34 charts.d.plugin: *charts.d.plugin*
35 python.d.plugin: *python.d.plugin*
36 systemd-journal.plugin: *systemd-journal.plugin*
37 network-viewer.plugin: *network-viewer.plugin*
38 +windows-events.plugin: *windows-events.plugin*
39 tc-qos-helper: *tc-qos-helper.sh*
40 fping: fping
41 ioping: ioping
@@ -93,262 +44,79 @@ slabinfo.plugin: *slabinfo.plugin*
44 ebpf.plugin: *ebpf.plugin*
45 debugfs.plugin: *debugfs.plugin*
46
96 -# agent-service-discovery
47 +## agent-service-discovery
48 agent_sd: agent_sd
49
99 -# -----------------------------------------------------------------------------
100 -# authentication/authorization related servers
101 -
102 -auth: radius* openldap* ldap* slapd authelia sssd saslauthd polkitd gssproxy
103 -fail2ban: fail2ban*
104 -
105 -# -----------------------------------------------------------------------------
106 -# web/ftp servers
50 +## -----------------------------------------------------------------------------
51
108 -httpd: apache* httpd nginx* lighttpd hiawatha caddy h2o
109 -proxy: squid* c-icap squidGuard varnish*
110 -php: php* lsphp*
111 -ftpd: proftpd in.tftpd vsftpd
112 -uwsgi: uwsgi
52 unicorn: *unicorn*
53 puma: *puma*
115 -
116 -# -----------------------------------------------------------------------------
117 -# database servers
118 -
119 -sql: mysqld* mariad* postgres* postmaster* oracle_* ora_* sqlservr
120 -nosql: mongod redis* valkey* memcached *couchdb*
121 -timedb: prometheus *carbon-cache.py* *carbon-aggregator.py* *graphite/manage.py* *net.opentsdb.tools.TSDMain* influxd*
122 -
123 -clickhouse: clickhouse-serv* clickhouse-cli* clckhouse-watch
124 -
125 -# -----------------------------------------------------------------------------
126 -# email servers
127 -
128 -mta: amavis* zmstat-* zmdiaglog zmmailboxdmgr opendkim postfwd2 smtp* lmtp* sendmail postfix master pickup qmgr showq tlsmgr postscreen oqmgr msmtp* nullmailer*
129 -mda: dovecot *imapd *pop3d *popd
130 -
131 -# -----------------------------------------------------------------------------
132 -# network, routing, VPN
133 -
134 -ppp: ppp*
135 -vpn: openvpn pptp* cjdroute gvpe tincd wireguard tailscaled
136 -wifi: hostapd wpa_supplicant
137 -routing: ospfd* ospf6d* bgpd bfdd fabricd isisd eigrpd sharpd staticd ripd ripngd pimd pbrd nhrpd ldpd zebra vrrpd vtysh bird*
138 -modem: ModemManager
139 -netmanager: NetworkManager nm* systemd-networkd networkctl netplan connmand wicked* avahi-autoipd networkd-dispatcher
140 -firewall: firewalld ufw nft
141 -tor: tor
142 -bluetooth: bluetooth bluetoothd bluez bluedevil obexd
143 -
144 -# -----------------------------------------------------------------------------
145 -# high availability and balancers
146 -
54 +couchdb: *couchdb*
55 +graphite: *carbon-cache.py* *carbon-aggregator.py* *graphite/manage.py*
56 +opentsdb: *net.opentsdb.tools.TSDMain*
57 +imapd: *imapd
58 +pop3d: *pop3d
59 +popd: *popd
60 camo: *camo*
148 -balancer: ipvs_* haproxy
149 -ha: corosync hs_logd ha_logd stonithd pacemakerd lrmd crmd keepalived ucarp*
150 -
151 -# -----------------------------------------------------------------------------
152 -# telephony
153 -
154 -pbx: asterisk safe_asterisk *vicidial*
155 -sip: opensips* stund
156 -
157 -# -----------------------------------------------------------------------------
158 -# chat
159 -
160 -chat: irssi *vines* *prosody* murmurd
161 -
162 -# -----------------------------------------------------------------------------
163 -# monitoring
164 -
165 -logs: ulogd* syslog* rsyslog* logrotate *systemd-journal* rotatelogs sysklogd metalog
166 -nms: snmpd vnstatd smokeping zabbix* munin* mon openhpid tailon nrpe
167 -monit: monit
168 -splunk: splunkd
61 +vicidial: *vicidial*
62 +vines: *vines*
63 +prosody: *prosody*
64 azure: mdsd *waagent* *omiserver* *omiagent* hv_kvp_daemon hv_vss_daemon *auoms* *omsagent*
65 datadog: *datadog*
171 -edgedelta: edgedelta
66 newrelic: newrelic*
67 google-agent: *google_guest_agent* *google_osconfig_agent*
174 -nvidia-smi: nvidia-smi
175 -intel_gpu_top: intel_gpu_top
176 -htop: htop
177 -watchdog: watchdog
178 -telegraf: telegraf
179 -grafana: grafana*
180 -
181 -# -----------------------------------------------------------------------------
182 -# storage, file systems and file servers
183 -
68 ceph: ceph-* ceph_* radosgw* rbd-* cephfs-* osdmaptool crushtool
69 samba: smbd nmbd winbindd ctdbd ctdb-* ctdb_*
70 nfs: rpcbind rpc.* nfs*
71 zfs: spl_* z_* txg_* zil_* arc_* l2arc*
188 -btrfs: btrfs*
72 iscsi: iscsid iscsi_eh
73 afp: netatalk afpd cnid_dbd cnid_metad
191 -ntfs-3g: ntfs-3g
192 -
193 -# -----------------------------------------------------------------------------
194 -# kubernetes
195 -
196 -kubelet: kubelet
197 -kube-dns: kube-dns
198 -kube-proxy: kube-proxy
199 -metrics-server: metrics-server
200 -heapster: heapster
201 -
202 -# -----------------------------------------------------------------------------
203 -# AWS
204 -
74 aws-s3: '*aws s3*' s3cmd s5cmd
206 -aws: aws
207 -
208 -# -----------------------------------------------------------------------------
209 -# virtualization platform
210 -
75 proxmox-ve: pve* spiceproxy
212 -
213 -# -----------------------------------------------------------------------------
214 -# containers & virtual machines
215 -
216 -containers: lxc* docker* balena* containerd
217 -VMs: vbox* VBox* qemu* kvm*
76 libvirt: virtlogd virtqemud virtstoraged virtnetworkd virtlockd virtinterfaced
77 libvirt: virtnodedevd virtproxyd virtsecretd libvirtd
78 guest-agent: qemu-ga spice-vdagent cloud-init*
221 -
222 -# -----------------------------------------------------------------------------
223 -# ssh servers and clients
224 -
225 -ssh: ssh* scp sftp* dropbear
226 -
227 -# -----------------------------------------------------------------------------
228 -# print servers and clients
229 -
230 -print: cups* lpd lpq
231 -
232 -# -----------------------------------------------------------------------------
233 -# time servers and clients
234 -
235 -time: ntp* systemd-timesyn* chronyd ptp*
236 -
237 -# -----------------------------------------------------------------------------
238 -# dhcp servers and clients
239 -
79 dhcp: *dhcp* dhclient
80
242 -# -----------------------------------------------------------------------------
243 -# name servers and clients
244 -
245 -dns: named unbound nsd pdns_server knotd gdnsd yadifad dnsmasq *systemd-resolve* pihole* avahi-daemon avahi-dnsconfd
246 -dnsdist: dnsdist
247 -
248 -# -----------------------------------------------------------------------------
249 -# installation / compilation / debugging
250 -
81 build: cc1 cc1plus as gcc* cppcheck ld make cmake automake autoconf autoreconf
82 build: cargo rustc bazel buck git gdb valgrind* rpmbuild dpkg-buildpackage
253 -
254 -# -----------------------------------------------------------------------------
255 -# package management
256 -
83 packagemanager: apt* dpkg* dselect dnf yum rpm zypp* yast* pacman xbps* swupd* emerge*
84 packagemanager: packagekitd pkgin pkg apk snapd slackpkg slapt-get
259 -
260 -# -----------------------------------------------------------------------------
261 -# antivirus
262 -
263 -antivirus: clam* *clam imunify360*
264 -
265 -# -----------------------------------------------------------------------------
266 -# torrent clients
267 -
268 -torrents: *deluge* transmission* *SickBeard* *CouchPotato* *rtorrent*
269 -
270 -# -----------------------------------------------------------------------------
271 -# backup servers and clients
272 -
85 +clam: clam* *clam
86 backup: rsync lsyncd bacula* borg rclone
274 -
275 -# -----------------------------------------------------------------------------
276 -# cron
277 -
87 cron: cron* atd anacron *systemd-cron* incrond
279 -
280 -# -----------------------------------------------------------------------------
281 -# UPS
282 -
88 ups: upsmon upsd */nut/* apcupsd
284 -
285 -# -----------------------------------------------------------------------------
286 -# media players, servers, clients
287 -
288 -media: mplayer vlc xine mediatomb omxplayer* kodi* xbmc* mediacenter eventlircd
289 -media: mpd minidlnad mt-daapd Plex* jellyfin squeeze* jackett Ombi
290 -media: strawberry* clementine*
291 -
89 audio: pulse* pipewire wireplumber jack*
90
294 -# -----------------------------------------------------------------------------
295 -# java applications
91 +rabbitmq: *rabbitmq*
92 +sidekiq: *sidekiq*
93 +erlang: beam.smp
94 +
95 +## -----------------------------------------------------------------------------
96 +## java applications
97
98 hdfsdatanode: *org.apache.hadoop.hdfs.server.datanode.DataNode*
99 hdfsnamenode: *org.apache.hadoop.hdfs.server.namenode.NameNode*
100 hdfsjournalnode: *org.apache.hadoop.hdfs.qjournal.server.JournalNode*
101 hdfszkfc: *org.apache.hadoop.hdfs.tools.DFSZKFailoverController*
301 -
102 yarnnode: *org.apache.hadoop.yarn.server.nodemanager.NodeManager*
103 yarnmgr: *org.apache.hadoop.yarn.server.resourcemanager.ResourceManager*
104 yarnproxy: *org.apache.hadoop.yarn.server.webproxy.WebAppProxyServer*
305 -
105 sparkworker: *org.apache.spark.deploy.worker.Worker*
106 sparkmaster: *org.apache.spark.deploy.master.Master*
308 -
107 hbaseregion: *org.apache.hadoop.hbase.regionserver.HRegionServer*
108 hbaserest: *org.apache.hadoop.hbase.rest.RESTServer*
109 hbasethrift: *org.apache.hadoop.hbase.thrift.ThriftServer*
110 hbasemaster: *org.apache.hadoop.hbase.master.HMaster*
313 -
111 zookeeper: *org.apache.zookeeper.server.quorum.QuorumPeerMain*
315 -
112 hive2: *org.apache.hive.service.server.HiveServer2*
113 hivemetastore: *org.apache.hadoop.hive.metastore.HiveMetaStore*
318 -
114 solr: *solr.install.dir*
320 -
115 airflow: *airflow*
116 +kafka: *kafka.Kafka*
117
323 -# -----------------------------------------------------------------------------
324 -# GUI
325 -
326 -X: X Xorg xinit xdm Xwayland xsettingsd touchegg
327 -wayland: swaylock swayidle waypipe wayvnc
328 -kde: *kdeinit* kdm sddm plasmashell startplasma-* kwin* kwallet* krunner kactivitymanager*
329 -gnome: gnome-* gdm gconf* mutter
330 -mate: mate-* msd-* marco*
331 -cinnamon: cinnamon* muffin
332 -xfce: xfwm4 xfdesktop xfce* Thunar xfsettingsd xfconf*
333 -lxde: lxde* startlxde lxdm lxappearance* lxlauncher* lxpanel* lxsession* lxsettings*
334 -lxqt: lxqt* startlxqt
335 -enlightenment: entrance enlightenment*
336 -i3: i3*
337 -awesome: awesome awesome-client
338 -dwm: dwm.*
339 -sway: sway
340 -weston: weston
341 -cage: cage
342 -wayfire: wayfire
343 -gui: lightdm colord seatd greetd gkrellm slim qingy dconf* *gvfs gvfs*
344 -gui: '*systemd --user*' xdg-* at-spi-*
345 -
346 -webbrowser: *chrome-sandbox* *google-chrome* *chromium* *firefox* vivaldi* opera* epiphany chrome*
347 -webbrowser: lynx elinks w3m w3mmee links
348 -mua: evolution-* thunderbird* mutt neomutt pine mailx alpine
349 -
350 -# -----------------------------------------------------------------------------
351 -# Kernel / System
118 +## -----------------------------------------------------------------------------
119 +## Kernel / System
120
121 ksmd: ksmd
122 khugepaged: khugepaged
@@ -356,87 +124,5 @@ kdamond: kdamond
124 kswapd: kswapd
125 zswap: zswap
126 kcompactd: kcompactd
359 -
360 -system: systemd* udisks* udevd* *udevd ipv6_addrconf dbus-* rtkit*
361 -system: mdadm acpid uuidd upowerd elogind* eudev mdev lvmpolld dmeventd
362 -system: accounts-daemon rngd haveged rasdaemon irqbalance start-stop-daemon
363 -system: supervise-daemon openrc* init runit runsvdir runsv auditd lsmd
364 -system: abrt* nscd rtkit-daemon gpg-agent usbguard* boltd geoclue
365 -
366 -kernel: kworker kthreadd kauditd lockd khelper kdevtmpfs khungtaskd rpciod
367 -kernel: fsnotify_mark kthrotld deferwq scsi_* kdmflush oom_reaper kdevtempfs
368 -kernel: ksoftirqd
369 -
370 -# -----------------------------------------------------------------------------
371 -# inetd
372 -
373 -inetd: inetd xinetd
374 -
375 -# -----------------------------------------------------------------------------
376 -# other application servers
377 -
378 -nginxunit: unitd
379 -
380 -typesense: typesense-serve
381 -
382 -i2pd: i2pd
383 -
384 -rethinkdb: rethinkdb
385 -
386 -beanstalkd: beanstalkd
387 -
388 -rspamd: rspamd
389 -
390 -consul: consul
391 -
392 -kafka: *kafka.Kafka*
393 -
394 -rabbitmq: *rabbitmq*
395 -
396 -sidekiq: *sidekiq*
397 -java: java
398 -ipfs: ipfs
399 -erlang: beam.smp
400 -
401 -node: node
402 -factorio: factorio
403 -
404 -p4: p4*
405 -
406 -git-services: gitea gitlab-runner
407 -
408 -freeswitch: freeswitch*
409 -
410 -# -------- web3 / blockchains ----------
411 -
412 -go-ethereum: geth*
413 -nethermind-ethereum: nethermind*
414 -besu-ethereum: besu*
415 -openEthereum: openethereum*
416 -urbit: urbit*
417 -bitcoin-node: *bitcoind* lnd*
418 -filecoin: lotus* lotus-miner* lotus-worker*
419 -solana: solana*
420 -web3: *hardhat* *ganache* *truffle* *brownie* *waffle*
421 -terra: terra* mantle*
422 -
423 -# -----------------------------------------------------------------------------
424 -# chaos engineering tools
425 -
426 -stress: stress stress-ng*
427 -gremlin: gremlin*
428 -
429 -# -----------------------------------------------------------------------------
430 -# load testing tools
431 -
432 -locust: locust
433 -
434 -# -----------------------------------------------------------------------------
435 -# data science and machine learning tools
436 -
437 -jupyter: jupyter*
438 -
439 -# -----------------------------------------------------------------------------
440 -# File synchronization tools
441 -
442 -filesync: dropbox syncthing
127 +ipvs: ipvs_*
128 +btrfs: btrfs*
src/collectors/apps.plugin/apps_incremental_collection.c new
+188
@@ -0,0 +1,188 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +#if (INCREMENTAL_DATA_COLLECTION == 1)
6 +bool managed_log(struct pid_stat *p, PID_LOG log, bool status) {
7 + if(unlikely(!status)) {
8 + // netdata_log_error("command failed log %u, errno %d", log, errno);
9 +
10 + if(unlikely(debug_enabled || errno != ENOENT)) {
11 + if(unlikely(debug_enabled || !(p->log_thrown & log))) {
12 + p->log_thrown |= log;
13 + switch(log) {
14 + case PID_LOG_IO:
15 +#if !defined(OS_LINUX)
16 + netdata_log_error("Cannot fetch process %d I/O info (command '%s')", p->pid, pid_stat_comm(p));
17 +#else
18 + netdata_log_error("Cannot process %s/proc/%d/io (command '%s')", netdata_configured_host_prefix, p->pid, pid_stat_comm(p));
19 +#endif
20 + break;
21 +
22 + case PID_LOG_STATUS:
23 +#if !defined(OS_LINUX)
24 + netdata_log_error("Cannot fetch process %d status info (command '%s')", p->pid, pid_stat_comm(p));
25 +#else
26 + netdata_log_error("Cannot process %s/proc/%d/status (command '%s')", netdata_configured_host_prefix, p->pid, pid_stat_comm(p));
27 +#endif
28 + break;
29 +
30 + case PID_LOG_CMDLINE:
31 +#if !defined(OS_LINUX)
32 + netdata_log_error("Cannot fetch process %d command line (command '%s')", p->pid, pid_stat_comm(p));
33 +#else
34 + netdata_log_error("Cannot process %s/proc/%d/cmdline (command '%s')", netdata_configured_host_prefix, p->pid, pid_stat_comm(p));
35 +#endif
36 + break;
37 +
38 + case PID_LOG_FDS:
39 +#if !defined(OS_LINUX)
40 + netdata_log_error("Cannot fetch process %d files (command '%s')", p->pid, pid_stat_comm(p));
41 +#else
42 + netdata_log_error("Cannot process entries in %s/proc/%d/fd (command '%s')", netdata_configured_host_prefix, p->pid, pid_stat_comm(p));
43 +#endif
44 + break;
45 +
46 + case PID_LOG_LIMITS:
47 +#if !defined(OS_LINUX)
48 + ;
49 +#else
50 + netdata_log_error("Cannot process %s/proc/%d/limits (command '%s')", netdata_configured_host_prefix, p->pid, pid_stat_comm(p));
51 +#endif
52 +
53 + case PID_LOG_STAT:
54 + break;
55 +
56 + default:
57 + netdata_log_error("unhandled error for pid %d, command '%s'", p->pid, pid_stat_comm(p));
58 + break;
59 + }
60 + }
61 + }
62 + errno_clear();
63 + }
64 + else if(unlikely(p->log_thrown & log)) {
65 + // netdata_log_error("unsetting log %u on pid %d", log, p->pid);
66 + p->log_thrown &= ~log;
67 + }
68 +
69 + return status;
70 +}
71 +
72 +static inline bool incrementally_read_pid_stat(struct pid_stat *p, void *ptr) {
73 + p->last_stat_collected_usec = p->stat_collected_usec;
74 + p->stat_collected_usec = now_monotonic_usec();
75 + calls_counter++;
76 +
77 + if(!OS_FUNCTION(apps_os_read_pid_stat)(p, ptr))
78 + return 0;
79 +
80 + return 1;
81 +}
82 +
83 +static inline int incrementally_read_pid_io(struct pid_stat *p, void *ptr) {
84 + p->last_io_collected_usec = p->io_collected_usec;
85 + p->io_collected_usec = now_monotonic_usec();
86 + calls_counter++;
87 +
88 + bool ret = OS_FUNCTION(apps_os_read_pid_io)(p, ptr);
89 +
90 + return ret ? 1 : 0;
91 +}
92 +
93 +// --------------------------------------------------------------------------------------------------------------------
94 +
95 +int incrementally_collect_data_for_pid_stat(struct pid_stat *p, void *ptr) {
96 + if(unlikely(p->read)) return 0;
97 +
98 + pid_collection_started(p);
99 +
100 + // --------------------------------------------------------------------
101 + // /proc/<pid>/stat
102 +
103 + if(unlikely(!managed_log(p, PID_LOG_STAT, incrementally_read_pid_stat(p, ptr)))) {
104 + // there is no reason to proceed if we cannot get its status
105 + pid_collection_failed(p);
106 + return 0;
107 + }
108 +
109 + // check its parent pid
110 + if(unlikely(p->ppid < INIT_PID))
111 + p->ppid = 0;
112 +
113 + // --------------------------------------------------------------------
114 + // /proc/<pid>/io
115 +
116 + managed_log(p, PID_LOG_IO, incrementally_read_pid_io(p, ptr));
117 +
118 + // --------------------------------------------------------------------
119 + // /proc/<pid>/status
120 +
121 + if(unlikely(!managed_log(p, PID_LOG_STATUS, OS_FUNCTION(apps_os_read_pid_status)(p, ptr)))) {
122 + // there is no reason to proceed if we cannot get its status
123 + pid_collection_failed(p);
124 + return 0;
125 + }
126 +
127 + // --------------------------------------------------------------------
128 + // /proc/<pid>/fd
129 +
130 +#if (PROCESSES_HAVE_FDS == 1)
131 + if(enable_file_charts) {
132 + managed_log(p, PID_LOG_FDS, read_pid_file_descriptors(p, ptr));
133 +#if (PROCESSES_HAVE_PID_LIMITS == 1)
134 + managed_log(p, PID_LOG_LIMITS, OS_FUNCTION(apps_os_read_pid_limits)(p, ptr));
135 +#endif
136 + }
137 +#endif
138 +
139 + // --------------------------------------------------------------------
140 + // done!
141 +
142 +#if defined(NETDATA_INTERNAL_CHECKS) && (ALL_PIDS_ARE_READ_INSTANTLY == 0)
143 + struct pid_stat *pp = p->parent;
144 + if(unlikely(include_exited_childs && pp && !pp->read))
145 + nd_log(NDLS_COLLECTORS, NDLP_WARNING,
146 + "Read process %d (%s) sortlisted %"PRIu32", but its parent %d (%s) sortlisted %"PRIu32", is not read",
147 + p->pid, pid_stat_comm(p), p->sortlist, pp->pid, pid_stat_comm(pp), pp->sortlist);
148 +#endif
149 +
150 + pid_collection_completed(p);
151 +
152 + return 1;
153 +}
154 +
155 +int incrementally_collect_data_for_pid(pid_t pid, void *ptr) {
156 + if(unlikely(pid < INIT_PID)) {
157 + netdata_log_error("Invalid pid %d read (expected >= %d). Ignoring process.", pid, INIT_PID);
158 + return 0;
159 + }
160 +
161 + struct pid_stat *p = get_or_allocate_pid_entry(pid);
162 + if(unlikely(!p)) return 0;
163 +
164 + return incrementally_collect_data_for_pid_stat(p, ptr);
165 +}
166 +#endif
167 +
168 +// --------------------------------------------------------------------------------------------------------------------
169 +
170 +#if (PROCESSES_HAVE_CMDLINE == 1)
171 +int read_proc_pid_cmdline(struct pid_stat *p) {
172 + static char cmdline[MAX_CMDLINE];
173 +
174 + if(unlikely(!OS_FUNCTION(apps_os_get_pid_cmdline)(p, cmdline, sizeof(cmdline))))
175 + goto cleanup;
176 +
177 + string_freez(p->cmdline);
178 + p->cmdline = string_strdupz(cmdline);
179 +
180 + return 1;
181 +
182 +cleanup:
183 + // copy the command to the command line
184 + string_freez(p->cmdline);
185 + p->cmdline = string_dup(p->comm);
186 + return 0;
187 +}
188 +#endif
src/collectors/apps.plugin/apps_os_freebsd.c new
+365
@@ -0,0 +1,365 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +#if defined(OS_FREEBSD)
6 +
7 +usec_t system_current_time_ut;
8 +long global_block_size = 512;
9 +
10 +static long get_fs_block_size(void) {
11 + struct statvfs vfs;
12 + static long block_size = 0;
13 +
14 + if (block_size == 0) {
15 + if (statvfs("/", &vfs) == 0) {
16 + block_size = vfs.f_frsize ? vfs.f_frsize : vfs.f_bsize;
17 + } else {
18 + // If statvfs fails, fall back to the typical block size
19 + block_size = 512;
20 + }
21 + }
22 +
23 + return block_size;
24 +}
25 +
26 +void apps_os_init_freebsd(void) {
27 + global_block_size = get_fs_block_size();
28 +}
29 +
30 +static inline void get_current_time(void) {
31 + struct timeval current_time;
32 + gettimeofday(&current_time, NULL);
33 + system_current_time_ut = timeval_usec(&current_time);
34 +}
35 +
36 +uint64_t apps_os_get_total_memory_freebsd(void) {
37 + uint64_t ret = 0;
38 +
39 + int mib[2] = {CTL_HW, HW_PHYSMEM};
40 + size_t size = sizeof(ret);
41 + if (sysctl(mib, 2, &ret, &size, NULL, 0) == -1) {
42 + netdata_log_error("Failed to get total memory using sysctl");
43 + return 0;
44 + }
45 +
46 + return ret;
47 +}
48 +
49 +bool apps_os_read_pid_fds_freebsd(struct pid_stat *p, void *ptr) {
50 + int mib[4];
51 + size_t size;
52 + struct kinfo_file *fds;
53 + static char *fdsbuf;
54 + char *bfdsbuf, *efdsbuf;
55 + char fdsname[FILENAME_MAX + 1];
56 +#define SHM_FORMAT_LEN 31 // format: 21 + size: 10
57 + char shm_name[FILENAME_MAX - SHM_FORMAT_LEN + 1];
58 +
59 + // we make all pid fds negative, so that
60 + // we can detect unused file descriptors
61 + // at the end, to free them
62 + make_all_pid_fds_negative(p);
63 +
64 + mib[0] = CTL_KERN;
65 + mib[1] = KERN_PROC;
66 + mib[2] = KERN_PROC_FILEDESC;
67 + mib[3] = p->pid;
68 +
69 + if (unlikely(sysctl(mib, 4, NULL, &size, NULL, 0))) {
70 + netdata_log_error("sysctl error: Can't get file descriptors data size for pid %d", p->pid);
71 + return false;
72 + }
73 + if (likely(size > 0))
74 + fdsbuf = reallocz(fdsbuf, size);
75 + if (unlikely(sysctl(mib, 4, fdsbuf, &size, NULL, 0))) {
76 + netdata_log_error("sysctl error: Can't get file descriptors data for pid %d", p->pid);
77 + return false;
78 + }
79 +
80 + bfdsbuf = fdsbuf;
81 + efdsbuf = fdsbuf + size;
82 + while (bfdsbuf < efdsbuf) {
83 + fds = (struct kinfo_file *)(uintptr_t)bfdsbuf;
84 + if (unlikely(fds->kf_structsize == 0))
85 + break;
86 +
87 + // do not process file descriptors for current working directory, root directory,
88 + // jail directory, ktrace vnode, text vnode and controlling terminal
89 + if (unlikely(fds->kf_fd < 0)) {
90 + bfdsbuf += fds->kf_structsize;
91 + continue;
92 + }
93 +
94 + // get file descriptors array index
95 + size_t fdid = fds->kf_fd;
96 +
97 + // check if the fds array is small
98 + if (unlikely(fdid >= p->fds_size)) {
99 + // it is small, extend it
100 +
101 + debug_log("extending fd memory slots for %s from %d to %d", pid_stat_comm(p), p->fds_size, fdid + MAX_SPARE_FDS);
102 +
103 + p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(struct pid_fd));
104 +
105 + // and initialize it
106 + init_pid_fds(p, p->fds_size, (fdid + MAX_SPARE_FDS) - p->fds_size);
107 + p->fds_size = fdid + MAX_SPARE_FDS;
108 + }
109 +
110 + if (unlikely(p->fds[fdid].fd == 0)) {
111 + // we don't know this fd, get it
112 +
113 + switch (fds->kf_type) {
114 + case KF_TYPE_FIFO:
115 + case KF_TYPE_VNODE:
116 + if (unlikely(!fds->kf_path[0])) {
117 + sprintf(fdsname, "other: inode: %lu", fds->kf_un.kf_file.kf_file_fileid);
118 + break;
119 + }
120 + sprintf(fdsname, "%s", fds->kf_path);
121 + break;
122 + case KF_TYPE_SOCKET:
123 + switch (fds->kf_sock_domain) {
124 + case AF_INET:
125 + case AF_INET6:
126 +#if __FreeBSD_version < 1400074
127 + if (fds->kf_sock_protocol == IPPROTO_TCP)
128 + sprintf(fdsname, "socket: %d %lx", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sock_inpcb);
129 + else
130 +#endif
131 + sprintf(fdsname, "socket: %d %lx", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sock_pcb);
132 + break;
133 + case AF_UNIX:
134 + /* print address of pcb and connected pcb */
135 + sprintf(fdsname, "socket: %lx %lx", fds->kf_un.kf_sock.kf_sock_pcb, fds->kf_un.kf_sock.kf_sock_unpconn);
136 + break;
137 + default:
138 + /* print protocol number and socket address */
139 +#if __FreeBSD_version < 1200031
140 + sprintf(fdsname, "socket: other: %d %s %s", fds->kf_sock_protocol, fds->kf_sa_local.__ss_pad1, fds->kf_sa_local.__ss_pad2);
141 +#else
142 + sprintf(fdsname, "socket: other: %d %s %s", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sa_local.__ss_pad1, fds->kf_un.kf_sock.kf_sa_local.__ss_pad2);
143 +#endif
144 + }
145 + break;
146 + case KF_TYPE_PIPE:
147 + sprintf(fdsname, "pipe: %lu %lu", fds->kf_un.kf_pipe.kf_pipe_addr, fds->kf_un.kf_pipe.kf_pipe_peer);
148 + break;
149 + case KF_TYPE_PTS:
150 +#if __FreeBSD_version < 1200031
151 + sprintf(fdsname, "other: pts: %u", fds->kf_un.kf_pts.kf_pts_dev);
152 +#else
153 + sprintf(fdsname, "other: pts: %lu", fds->kf_un.kf_pts.kf_pts_dev);
154 +#endif
155 + break;
156 + case KF_TYPE_SHM:
157 + strncpyz(shm_name, fds->kf_path, FILENAME_MAX - SHM_FORMAT_LEN);
158 + sprintf(fdsname, "other: shm: %s size: %lu", shm_name, fds->kf_un.kf_file.kf_file_size);
159 + break;
160 + case KF_TYPE_SEM:
161 + sprintf(fdsname, "other: sem: %u", fds->kf_un.kf_sem.kf_sem_value);
162 + break;
163 + default:
164 + sprintf(fdsname, "other: pid: %d fd: %d", fds->kf_un.kf_proc.kf_pid, fds->kf_fd);
165 + }
166 +
167 + // if another process already has this, we will get
168 + // the same id
169 + p->fds[fdid].fd = file_descriptor_find_or_add(fdsname, 0);
170 + }
171 +
172 + // else make it positive again, we need it
173 + // of course, the actual file may have changed
174 +
175 + else
176 + p->fds[fdid].fd = -p->fds[fdid].fd;
177 +
178 + bfdsbuf += fds->kf_structsize;
179 + }
180 +
181 + return true;
182 +}
183 +
184 +bool apps_os_get_pid_cmdline_freebsd(struct pid_stat *p, char *cmdline, size_t bytes) {
185 + size_t i, b = bytes - 1;
186 + int mib[4];
187 +
188 + mib[0] = CTL_KERN;
189 + mib[1] = KERN_PROC;
190 + mib[2] = KERN_PROC_ARGS;
191 + mib[3] = p->pid;
192 + if (unlikely(sysctl(mib, 4, cmdline, &b, NULL, 0)))
193 + return false;
194 +
195 + cmdline[b] = '\0';
196 + for(i = 0; i < b ; i++)
197 + if(unlikely(!cmdline[i])) cmdline[i] = ' ';
198 +
199 + return true;
200 +}
201 +
202 +bool apps_os_read_pid_io_freebsd(struct pid_stat *p, void *ptr) {
203 + struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr;
204 +
205 + pid_incremental_rate(io, PDF_LREAD, proc_info->ki_rusage.ru_inblock * global_block_size);
206 + pid_incremental_rate(io, PDF_LWRITE, proc_info->ki_rusage.ru_oublock * global_block_size);
207 +
208 + return true;
209 +}
210 +
211 +bool apps_os_read_pid_limits_freebsd(struct pid_stat *p __maybe_unused, void *ptr __maybe_unused) {
212 + return false;
213 +}
214 +
215 +bool apps_os_read_pid_status_freebsd(struct pid_stat *p, void *ptr) {
216 + struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr;
217 +
218 + p->uid = proc_info->ki_uid;
219 + p->gid = proc_info->ki_groups[0];
220 + p->values[PDF_VMSIZE] = proc_info->ki_size;
221 + p->values[PDF_VMRSS] = proc_info->ki_rssize * pagesize;
222 + // TODO: what about shared and swap memory on FreeBSD?
223 + return true;
224 +}
225 +
226 +//bool apps_os_read_global_cpu_utilization_freebsd(void) {
227 +// static kernel_uint_t utime_raw = 0, stime_raw = 0, ntime_raw = 0;
228 +// static usec_t collected_usec = 0, last_collected_usec = 0;
229 +// long cp_time[CPUSTATES];
230 +//
231 +// if (unlikely(CPUSTATES != 5)) {
232 +// goto cleanup;
233 +// } else {
234 +// static int mib[2] = {0, 0};
235 +//
236 +// if (unlikely(GETSYSCTL_SIMPLE("kern.cp_time", mib, cp_time))) {
237 +// goto cleanup;
238 +// }
239 +// }
240 +//
241 +// last_collected_usec = collected_usec;
242 +// collected_usec = now_monotonic_usec();
243 +//
244 +// calls_counter++;
245 +//
246 +// // temporary - it is added global_ntime;
247 +// kernel_uint_t global_ntime = 0;
248 +//
249 +// incremental_rate(global_utime, utime_raw, cp_time[0], collected_usec, last_collected_usec, (NSEC_PER_SEC / system_hz));
250 +// incremental_rate(global_ntime, ntime_raw, cp_time[1], collected_usec, last_collected_usec, (NSEC_PER_SEC / system_hz));
251 +// incremental_rate(global_stime, stime_raw, cp_time[2], collected_usec, last_collected_usec, (NSEC_PER_SEC / system_hz));
252 +//
253 +// global_utime += global_ntime;
254 +//
255 +// if(unlikely(global_iterations_counter == 1)) {
256 +// global_utime = 0;
257 +// global_stime = 0;
258 +// global_gtime = 0;
259 +// }
260 +//
261 +// return 1;
262 +//
263 +//cleanup:
264 +// global_utime = 0;
265 +// global_stime = 0;
266 +// global_gtime = 0;
267 +// return 0;
268 +//}
269 +
270 +bool apps_os_read_pid_stat_freebsd(struct pid_stat *p, void *ptr) {
271 + struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr;
272 + if (unlikely(proc_info->ki_tdflags & TDF_IDLETD))
273 + goto cleanup;
274 +
275 + char *comm = proc_info->ki_comm;
276 + p->ppid = proc_info->ki_ppid;
277 +
278 + update_pid_comm(p, comm);
279 +
280 + pid_incremental_rate(stat, PDF_MINFLT, (kernel_uint_t)proc_info->ki_rusage.ru_minflt);
281 + pid_incremental_rate(stat, PDF_CMINFLT, (kernel_uint_t)proc_info->ki_rusage_ch.ru_minflt);
282 + pid_incremental_rate(stat, PDF_MAJFLT, (kernel_uint_t)proc_info->ki_rusage.ru_majflt);
283 + pid_incremental_rate(stat, PDF_CMAJFLT, (kernel_uint_t)proc_info->ki_rusage_ch.ru_majflt);
284 + pid_incremental_rate(stat, PDF_UTIME, (kernel_uint_t)proc_info->ki_rusage.ru_utime.tv_sec * NSEC_PER_SEC + proc_info->ki_rusage.ru_utime.tv_usec * NSEC_PER_USEC);
285 + pid_incremental_rate(stat, PDF_STIME, (kernel_uint_t)proc_info->ki_rusage.ru_stime.tv_sec * NSEC_PER_SEC + proc_info->ki_rusage.ru_stime.tv_usec * NSEC_PER_USEC);
286 + pid_incremental_rate(stat, PDF_CUTIME, (kernel_uint_t)proc_info->ki_rusage_ch.ru_utime.tv_sec * NSEC_PER_SEC + proc_info->ki_rusage_ch.ru_utime.tv_usec * NSEC_PER_USEC);
287 + pid_incremental_rate(stat, PDF_CSTIME, (kernel_uint_t)proc_info->ki_rusage_ch.ru_stime.tv_sec * NSEC_PER_SEC + proc_info->ki_rusage_ch.ru_stime.tv_usec * NSEC_PER_USEC);
288 +
289 + p->values[PDF_THREADS] = proc_info->ki_numthreads;
290 +
291 + usec_t started_ut = timeval_usec(&proc_info->ki_start);
292 + p->values[PDF_UPTIME] = (system_current_time_ut > started_ut) ? (system_current_time_ut - started_ut) / USEC_PER_SEC : 0;
293 +
294 + if(unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
295 + debug_log_int("READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT ", threads=%d",
296 + netdata_configured_host_prefix, p->pid, pid_stat_comm(p), (p->target)?string2str(p->target->name):"UNSET",
297 + p->stat_collected_usec - p->last_stat_collected_usec,
298 + p->values[PDF_UTIME],
299 + p->values[PDF_STIME],
300 + p->values[PDF_CUTIME],
301 + p->values[PDF_CSTIME],
302 + p->values[PDF_MINFLT],
303 + p->values[PDF_MAJFLT],
304 + p->values[PDF_CMINFLT],
305 + p->values[PDF_CMAJFLT],
306 + p->values[PDF_THREADS]);
307 +
308 + return true;
309 +
310 +cleanup:
311 + return false;
312 +}
313 +
314 +bool apps_os_collect_all_pids_freebsd(void) {
315 + // Mark all processes as unread before collecting new data
316 + struct pid_stat *p = NULL;
317 + int i, procnum;
318 +
319 + static size_t procbase_size = 0;
320 + static struct kinfo_proc *procbase = NULL;
321 +
322 + size_t new_procbase_size;
323 +
324 + int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC };
325 + if (unlikely(sysctl(mib, 3, NULL, &new_procbase_size, NULL, 0))) {
326 + netdata_log_error("sysctl error: Can't get processes data size");
327 + return false;
328 + }
329 +
330 + // give it some air for processes that may be started
331 + // during this little time.
332 + new_procbase_size += 100 * sizeof(struct kinfo_proc);
333 +
334 + // increase the buffer if needed
335 + if(new_procbase_size > procbase_size) {
336 + procbase_size = new_procbase_size;
337 + procbase = reallocz(procbase, procbase_size);
338 + }
339 +
340 + // sysctl() gets from new_procbase_size the buffer size
341 + // and also returns to it the amount of data filled in
342 + new_procbase_size = procbase_size;
343 +
344 + // get the processes from the system
345 + if (unlikely(sysctl(mib, 3, procbase, &new_procbase_size, NULL, 0))) {
346 + netdata_log_error("sysctl error: Can't get processes data");
347 + return false;
348 + }
349 +
350 + // based on the amount of data filled in
351 + // calculate the number of processes we got
352 + procnum = new_procbase_size / sizeof(struct kinfo_proc);
353 +
354 + get_current_time();
355 +
356 + for (i = 0 ; i < procnum ; ++i) {
357 + pid_t pid = procbase[i].ki_pid;
358 + if (pid <= 0) continue;
359 + incrementally_collect_data_for_pid(pid, &procbase[i]);
360 + }
361 +
362 + return true;
363 +}
364 +
365 +#endif
src/collectors/apps.plugin/apps_os_linux.c new
+771
@@ -0,0 +1,771 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +#if defined(OS_LINUX)
6 +
7 +#define MAX_PROC_PID_LIMITS 8192
8 +#define PROC_PID_LIMITS_MAX_OPEN_FILES_KEY "\nMax open files "
9 +
10 +int max_fds_cache_seconds = 60;
11 +kernel_uint_t system_uptime_secs;
12 +
13 +void apps_os_init_linux(void) {
14 + ;
15 +}
16 +
17 +// --------------------------------------------------------------------------------------------------------------------
18 +// /proc/pid/fd
19 +
20 +struct arl_callback_ptr {
21 + struct pid_stat *p;
22 + procfile *ff;
23 + size_t line;
24 +};
25 +
26 +bool apps_os_read_pid_fds_linux(struct pid_stat *p, void *ptr __maybe_unused) {
27 + if(unlikely(!p->fds_dirname)) {
28 + char dirname[FILENAME_MAX+1];
29 + snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", netdata_configured_host_prefix, p->pid);
30 + p->fds_dirname = strdupz(dirname);
31 + }
32 +
33 + DIR *fds = opendir(p->fds_dirname);
34 + if(unlikely(!fds)) return false;
35 +
36 + struct dirent *de;
37 + char linkname[FILENAME_MAX + 1];
38 +
39 + // we make all pid fds negative, so that
40 + // we can detect unused file descriptors
41 + // at the end, to free them
42 + make_all_pid_fds_negative(p);
43 +
44 + while((de = readdir(fds))) {
45 + // we need only files with numeric names
46 +
47 + if(unlikely(de->d_name[0] < '0' || de->d_name[0] > '9'))
48 + continue;
49 +
50 + // get its number
51 + int fdid = (int) str2l(de->d_name);
52 + if(unlikely(fdid < 0)) continue;
53 +
54 + // check if the fds array is small
55 + if(unlikely((size_t)fdid >= p->fds_size)) {
56 + // it is small, extend it
57 +
58 + debug_log("extending fd memory slots for %s from %d to %d"
59 + , pid_stat_comm(p)
60 + , p->fds_size
61 + , fdid + MAX_SPARE_FDS
62 + );
63 +
64 + p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(struct pid_fd));
65 +
66 + // and initialize it
67 + init_pid_fds(p, p->fds_size, (fdid + MAX_SPARE_FDS) - p->fds_size);
68 + p->fds_size = (size_t)fdid + MAX_SPARE_FDS;
69 + }
70 +
71 + if(unlikely(p->fds[fdid].fd < 0 && de->d_ino != p->fds[fdid].inode)) {
72 + // inodes do not match, clear the previous entry
73 + inodes_changed_counter++;
74 + file_descriptor_not_used(-p->fds[fdid].fd);
75 + clear_pid_fd(&p->fds[fdid]);
76 + }
77 +
78 + if(p->fds[fdid].fd < 0 && p->fds[fdid].cache_iterations_counter > 0) {
79 + p->fds[fdid].fd = -p->fds[fdid].fd;
80 + p->fds[fdid].cache_iterations_counter--;
81 + continue;
82 + }
83 +
84 + if(unlikely(!p->fds[fdid].filename)) {
85 + filenames_allocated_counter++;
86 + char fdname[FILENAME_MAX + 1];
87 + snprintfz(fdname, FILENAME_MAX, "%s/proc/%d/fd/%s", netdata_configured_host_prefix, p->pid, de->d_name);
88 + p->fds[fdid].filename = strdupz(fdname);
89 + }
90 +
91 + file_counter++;
92 + ssize_t l = readlink(p->fds[fdid].filename, linkname, FILENAME_MAX);
93 + if(unlikely(l == -1)) {
94 + // cannot read the link
95 +
96 + if(debug_enabled || (p->target && p->target->debug_enabled))
97 + netdata_log_error("Cannot read link %s", p->fds[fdid].filename);
98 +
99 + if(unlikely(p->fds[fdid].fd < 0)) {
100 + file_descriptor_not_used(-p->fds[fdid].fd);
101 + clear_pid_fd(&p->fds[fdid]);
102 + }
103 +
104 + continue;
105 + }
106 + else
107 + linkname[l] = '\0';
108 +
109 + uint32_t link_hash = simple_hash(linkname);
110 +
111 + if(unlikely(p->fds[fdid].fd < 0 && p->fds[fdid].link_hash != link_hash)) {
112 + // the link changed
113 + links_changed_counter++;
114 + file_descriptor_not_used(-p->fds[fdid].fd);
115 + clear_pid_fd(&p->fds[fdid]);
116 + }
117 +
118 + if(unlikely(p->fds[fdid].fd == 0)) {
119 + // we don't know this fd, get it
120 +
121 + // if another process already has this, we will get
122 + // the same id
123 + p->fds[fdid].fd = (int)file_descriptor_find_or_add(linkname, link_hash);
124 + p->fds[fdid].inode = de->d_ino;
125 + p->fds[fdid].link_hash = link_hash;
126 + }
127 + else {
128 + // else make it positive again, we need it
129 + p->fds[fdid].fd = -p->fds[fdid].fd;
130 + }
131 +
132 + // caching control
133 + // without this we read all the files on every iteration
134 + if(max_fds_cache_seconds > 0) {
135 + size_t spread = ((size_t)max_fds_cache_seconds > 10) ? 10 : (size_t)max_fds_cache_seconds;
136 +
137 + // cache it for a few iterations
138 + size_t max = ((size_t) max_fds_cache_seconds + (fdid % spread)) / (size_t) update_every;
139 + p->fds[fdid].cache_iterations_reset++;
140 +
141 + if(unlikely(p->fds[fdid].cache_iterations_reset % spread == (size_t) fdid % spread))
142 + p->fds[fdid].cache_iterations_reset++;
143 +
144 + if(unlikely((fdid <= 2 && p->fds[fdid].cache_iterations_reset > 5) ||
145 + p->fds[fdid].cache_iterations_reset > max)) {
146 + // for stdin, stdout, stderr (fdid <= 2) we have checked a few times, or if it goes above the max, goto max
147 + p->fds[fdid].cache_iterations_reset = max;
148 + }
149 +
150 + p->fds[fdid].cache_iterations_counter = p->fds[fdid].cache_iterations_reset;
151 + }
152 + }
153 +
154 + closedir(fds);
155 +
156 + return true;
157 +}
158 +
159 +// --------------------------------------------------------------------------------------------------------------------
160 +// /proc/meminfo
161 +
162 +uint64_t apps_os_get_total_memory_linux(void) {
163 + uint64_t ret = 0;
164 +
165 + char filename[FILENAME_MAX + 1];
166 + snprintfz(filename, FILENAME_MAX, "%s/proc/meminfo", netdata_configured_host_prefix);
167 +
168 + procfile *ff = procfile_open(filename, ": \t", PROCFILE_FLAG_DEFAULT);
169 + if(!ff)
170 + return ret;
171 +
172 + ff = procfile_readall(ff);
173 + if(!ff)
174 + return ret;
175 +
176 + size_t line, lines = procfile_lines(ff);
177 +
178 + for(line = 0; line < lines ;line++) {
179 + size_t words = procfile_linewords(ff, line);
180 + if(words == 3 && strcmp(procfile_lineword(ff, line, 0), "MemTotal") == 0 && strcmp(procfile_lineword(ff, line, 2), "kB") == 0) {
181 + ret = str2ull(procfile_lineword(ff, line, 1), NULL) * 1024;
182 + break;
183 + }
184 + }
185 +
186 + procfile_close(ff);
187 +
188 + return ret;
189 +}
190 +
191 +// --------------------------------------------------------------------------------------------------------------------
192 +// /proc/pid/cmdline
193 +
194 +bool apps_os_get_pid_cmdline_linux(struct pid_stat *p, char *cmdline, size_t bytes) {
195 + if(unlikely(!p->cmdline_filename)) {
196 + char filename[FILENAME_MAX];
197 + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", netdata_configured_host_prefix, p->pid);
198 + p->cmdline_filename = strdupz(filename);
199 + }
200 +
201 + int fd = open(p->cmdline_filename, procfile_open_flags, 0666);
202 + if(unlikely(fd == -1))
203 + return false;
204 +
205 + ssize_t i, b = read(fd, cmdline, bytes - 1);
206 + close(fd);
207 +
208 + if(unlikely(b < 0))
209 + return false;
210 +
211 + cmdline[b] = '\0';
212 + for(i = 0; i < b ; i++)
213 + if(unlikely(!cmdline[i])) cmdline[i] = ' ';
214 +
215 + // remove trailing spaces
216 + while(b > 0 && cmdline[b - 1] == ' ')
217 + cmdline[--b] = '\0';
218 +
219 + return true;
220 +}
221 +
222 +// --------------------------------------------------------------------------------------------------------------------
223 +// /proc/pid/io
224 +
225 +bool apps_os_read_pid_io_linux(struct pid_stat *p, void *ptr __maybe_unused) {
226 + static procfile *ff = NULL;
227 +
228 + if(unlikely(!p->io_filename)) {
229 + char filename[FILENAME_MAX + 1];
230 + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", netdata_configured_host_prefix, p->pid);
231 + p->io_filename = strdupz(filename);
232 + }
233 +
234 + // open the file
235 + ff = procfile_reopen(ff, p->io_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
236 + if(unlikely(!ff)) goto cleanup;
237 +
238 + ff = procfile_readall(ff);
239 + if(unlikely(!ff)) goto cleanup;
240 +
241 + pid_incremental_rate(io, PDF_LREAD, str2kernel_uint_t(procfile_lineword(ff, 0, 1)));
242 + pid_incremental_rate(io, PDF_LWRITE, str2kernel_uint_t(procfile_lineword(ff, 1, 1)));
243 + pid_incremental_rate(io, PDF_OREAD, str2kernel_uint_t(procfile_lineword(ff, 2, 1)));
244 + pid_incremental_rate(io, PDF_OWRITE, str2kernel_uint_t(procfile_lineword(ff, 3, 1)));
245 + pid_incremental_rate(io, PDF_PREAD, str2kernel_uint_t(procfile_lineword(ff, 4, 1)));
246 + pid_incremental_rate(io, PDF_PWRITE, str2kernel_uint_t(procfile_lineword(ff, 5, 1)));
247 +
248 + return true;
249 +
250 +cleanup:
251 + return false;
252 +}
253 +
254 +// --------------------------------------------------------------------------------------------------------------------
255 +// /proc/pid/limits
256 +
257 +static inline kernel_uint_t get_proc_pid_limits_limit(char *buf, const char *key, size_t key_len, kernel_uint_t def) {
258 + char *line = strstr(buf, key);
259 + if(!line)
260 + return def;
261 +
262 + char *v = &line[key_len];
263 + while(isspace((uint8_t)*v)) v++;
264 +
265 + if(strcmp(v, "unlimited") == 0)
266 + return 0;
267 +
268 + return str2ull(v, NULL);
269 +}
270 +
271 +bool apps_os_read_pid_limits_linux(struct pid_stat *p, void *ptr __maybe_unused) {
272 + static char proc_pid_limits_buffer[MAX_PROC_PID_LIMITS + 1];
273 + bool ret = false;
274 + bool read_limits = false;
275 +
276 + errno_clear();
277 + proc_pid_limits_buffer[0] = '\0';
278 +
279 + kernel_uint_t all_fds = pid_openfds_sum(p);
280 + if(all_fds < p->limits.max_open_files / 2 && p->io_collected_usec > p->last_limits_collected_usec && p->io_collected_usec - p->last_limits_collected_usec <= 60 * USEC_PER_SEC) {
281 + // too frequent, we want to collect limits once per minute
282 + ret = true;
283 + goto cleanup;
284 + }
285 +
286 + if(unlikely(!p->limits_filename)) {
287 + char filename[FILENAME_MAX + 1];
288 + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/limits", netdata_configured_host_prefix, p->pid);
289 + p->limits_filename = strdupz(filename);
290 + }
291 +
292 + int fd = open(p->limits_filename, procfile_open_flags, 0666);
293 + if(unlikely(fd == -1)) goto cleanup;
294 +
295 + ssize_t bytes = read(fd, proc_pid_limits_buffer, MAX_PROC_PID_LIMITS);
296 + close(fd);
297 +
298 + if(bytes <= 0)
299 + goto cleanup;
300 +
301 + // make it '\0' terminated
302 + if(bytes < MAX_PROC_PID_LIMITS)
303 + proc_pid_limits_buffer[bytes] = '\0';
304 + else
305 + proc_pid_limits_buffer[MAX_PROC_PID_LIMITS - 1] = '\0';
306 +
307 + p->limits.max_open_files = get_proc_pid_limits_limit(proc_pid_limits_buffer, PROC_PID_LIMITS_MAX_OPEN_FILES_KEY, sizeof(PROC_PID_LIMITS_MAX_OPEN_FILES_KEY) - 1, 0);
308 + if(p->limits.max_open_files == 1) {
309 + // it seems a bug in the kernel or something similar
310 + // it sets max open files to 1 but the number of files
311 + // the process has open are more than 1...
312 + // https://github.com/netdata/netdata/issues/15443
313 + p->limits.max_open_files = 0;
314 + ret = true;
315 + goto cleanup;
316 + }
317 +
318 + p->last_limits_collected_usec = p->io_collected_usec;
319 + read_limits = true;
320 +
321 + ret = true;
322 +
323 +cleanup:
324 + if(p->limits.max_open_files)
325 + p->openfds_limits_percent = (NETDATA_DOUBLE)all_fds * 100.0 / (NETDATA_DOUBLE)p->limits.max_open_files;
326 + else
327 + p->openfds_limits_percent = 0.0;
328 +
329 + if(p->openfds_limits_percent > 100.0) {
330 + if(!(p->log_thrown & PID_LOG_LIMITS_DETAIL)) {
331 + char *line;
332 +
333 + if(!read_limits) {
334 + proc_pid_limits_buffer[0] = '\0';
335 + line = "NOT READ";
336 + }
337 + else {
338 + line = strstr(proc_pid_limits_buffer, PROC_PID_LIMITS_MAX_OPEN_FILES_KEY);
339 + if (line) {
340 + line++; // skip the initial newline
341 +
342 + char *end = strchr(line, '\n');
343 + if (end)
344 + *end = '\0';
345 + }
346 + }
347 +
348 + netdata_log_info(
349 + "FDS_LIMITS: PID %d (%s) is using "
350 + "%0.2f %% of its fds limits, "
351 + "open fds = %"PRIu64 "("
352 + "files = %"PRIu64 ", "
353 + "pipes = %"PRIu64 ", "
354 + "sockets = %"PRIu64", "
355 + "inotifies = %"PRIu64", "
356 + "eventfds = %"PRIu64", "
357 + "timerfds = %"PRIu64", "
358 + "signalfds = %"PRIu64", "
359 + "eventpolls = %"PRIu64" "
360 + "other = %"PRIu64" "
361 + "), open fds limit = %"PRIu64", "
362 + "%s, "
363 + "original line [%s]",
364 + p->pid, pid_stat_comm(p), p->openfds_limits_percent, all_fds,
365 + p->openfds.files,
366 + p->openfds.pipes,
367 + p->openfds.sockets,
368 + p->openfds.inotifies,
369 + p->openfds.eventfds,
370 + p->openfds.timerfds,
371 + p->openfds.signalfds,
372 + p->openfds.eventpolls,
373 + p->openfds.other,
374 + p->limits.max_open_files,
375 + read_limits ? "and we have read the limits AFTER counting the fds"
376 + : "but we have read the limits BEFORE counting the fds",
377 + line);
378 +
379 + p->log_thrown |= PID_LOG_LIMITS_DETAIL;
380 + }
381 + }
382 + else
383 + p->log_thrown &= ~PID_LOG_LIMITS_DETAIL;
384 +
385 + return ret;
386 +}
387 +
388 +// --------------------------------------------------------------------------------------------------------------------
389 +// /proc/pid/status
390 +
391 +void arl_callback_status_uid(const char *name, uint32_t hash, const char *value, void *dst) {
392 + (void)name; (void)hash; (void)value;
393 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
394 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 5)) return;
395 +
396 + //const char *real_uid = procfile_lineword(aptr->ff, aptr->line, 1);
397 + const char *effective_uid = procfile_lineword(aptr->ff, aptr->line, 2);
398 + //const char *saved_uid = procfile_lineword(aptr->ff, aptr->line, 3);
399 + //const char *filesystem_uid = procfile_lineword(aptr->ff, aptr->line, 4);
400 +
401 + if(likely(effective_uid && *effective_uid))
402 + aptr->p->uid = (uid_t)str2l(effective_uid);
403 +}
404 +
405 +void arl_callback_status_gid(const char *name, uint32_t hash, const char *value, void *dst) {
406 + (void)name; (void)hash; (void)value;
407 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
408 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 5)) return;
409 +
410 + //const char *real_gid = procfile_lineword(aptr->ff, aptr->line, 1);
411 + const char *effective_gid = procfile_lineword(aptr->ff, aptr->line, 2);
412 + //const char *saved_gid = procfile_lineword(aptr->ff, aptr->line, 3);
413 + //const char *filesystem_gid = procfile_lineword(aptr->ff, aptr->line, 4);
414 +
415 + if(likely(effective_gid && *effective_gid))
416 + aptr->p->gid = (uid_t)str2l(effective_gid);
417 +}
418 +
419 +void arl_callback_status_vmsize(const char *name, uint32_t hash, const char *value, void *dst) {
420 + (void)name; (void)hash; (void)value;
421 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
422 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
423 +
424 + aptr->p->values[PDF_VMSIZE] = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1)) * 1024;
425 +}
426 +
427 +void arl_callback_status_vmswap(const char *name, uint32_t hash, const char *value, void *dst) {
428 + (void)name; (void)hash; (void)value;
429 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
430 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
431 +
432 + aptr->p->values[PDF_VMSWAP] = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1)) * 1024;
433 +}
434 +
435 +void arl_callback_status_vmrss(const char *name, uint32_t hash, const char *value, void *dst) {
436 + (void)name; (void)hash; (void)value;
437 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
438 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
439 +
440 + aptr->p->values[PDF_VMRSS] = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1)) * 1024;
441 +}
442 +
443 +void arl_callback_status_rssfile(const char *name, uint32_t hash, const char *value, void *dst) {
444 + (void)name; (void)hash; (void)value;
445 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
446 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
447 +
448 + aptr->p->values[PDF_RSSFILE] = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1)) * 1024;
449 +}
450 +
451 +void arl_callback_status_rssshmem(const char *name, uint32_t hash, const char *value, void *dst) {
452 + (void)name; (void)hash; (void)value;
453 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
454 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
455 +
456 + aptr->p->values[PDF_RSSSHMEM] = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1)) * 1024;
457 +}
458 +
459 +void arl_callback_status_voluntary_ctxt_switches(const char *name, uint32_t hash, const char *value, void *dst) {
460 + (void)name; (void)hash; (void)value;
461 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
462 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 2)) return;
463 +
464 + struct pid_stat *p = aptr->p;
465 + pid_incremental_rate(stat, PDF_VOLCTX, str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1)));
466 +}
467 +
468 +void arl_callback_status_nonvoluntary_ctxt_switches(const char *name, uint32_t hash, const char *value, void *dst) {
469 + (void)name; (void)hash; (void)value;
470 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
471 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 2)) return;
472 +
473 + struct pid_stat *p = aptr->p;
474 + pid_incremental_rate(stat, PDF_NVOLCTX, str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1)));
475 +}
476 +
477 +bool apps_os_read_pid_status_linux(struct pid_stat *p, void *ptr __maybe_unused) {
478 + static struct arl_callback_ptr arl_ptr;
479 + static procfile *ff = NULL;
480 +
481 + if(unlikely(!p->status_arl)) {
482 + p->status_arl = arl_create("/proc/pid/status", NULL, 60);
483 + arl_expect_custom(p->status_arl, "Uid", arl_callback_status_uid, &arl_ptr);
484 + arl_expect_custom(p->status_arl, "Gid", arl_callback_status_gid, &arl_ptr);
485 + arl_expect_custom(p->status_arl, "VmSize", arl_callback_status_vmsize, &arl_ptr);
486 + arl_expect_custom(p->status_arl, "VmRSS", arl_callback_status_vmrss, &arl_ptr);
487 + arl_expect_custom(p->status_arl, "RssFile", arl_callback_status_rssfile, &arl_ptr);
488 + arl_expect_custom(p->status_arl, "RssShmem", arl_callback_status_rssshmem, &arl_ptr);
489 + arl_expect_custom(p->status_arl, "VmSwap", arl_callback_status_vmswap, &arl_ptr);
490 + arl_expect_custom(p->status_arl, "voluntary_ctxt_switches", arl_callback_status_voluntary_ctxt_switches, &arl_ptr);
491 + arl_expect_custom(p->status_arl, "nonvoluntary_ctxt_switches", arl_callback_status_nonvoluntary_ctxt_switches, &arl_ptr);
492 + }
493 +
494 + if(unlikely(!p->status_filename)) {
495 + char filename[FILENAME_MAX + 1];
496 + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/status", netdata_configured_host_prefix, p->pid);
497 + p->status_filename = strdupz(filename);
498 + }
499 +
500 + ff = procfile_reopen(ff, p->status_filename, (!ff)?" \t:,-()/":NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
501 + if(unlikely(!ff)) return false;
502 +
503 + ff = procfile_readall(ff);
504 + if(unlikely(!ff)) return false;
505 +
506 + calls_counter++;
507 +
508 + // let ARL use this pid
509 + arl_ptr.p = p;
510 + arl_ptr.ff = ff;
511 +
512 + size_t lines = procfile_lines(ff), l;
513 + arl_begin(p->status_arl);
514 +
515 + for(l = 0; l < lines ;l++) {
516 + // debug_log("CHECK: line %zu of %zu, key '%s' = '%s'", l, lines, procfile_lineword(ff, l, 0), procfile_lineword(ff, l, 1));
517 + arl_ptr.line = l;
518 + if(unlikely(arl_check(p->status_arl,
519 + procfile_lineword(ff, l, 0),
520 + procfile_lineword(ff, l, 1)))) break;
521 + }
522 +
523 + p->values[PDF_VMSHARED] = p->values[PDF_RSSFILE] + p->values[PDF_RSSSHMEM];
524 + return true;
525 +}
526 +
527 +// --------------------------------------------------------------------------------------------------------------------
528 +// global CPU utilization
529 +
530 +bool apps_os_read_global_cpu_utilization_linux(void) {
531 + static char filename[FILENAME_MAX + 1] = "";
532 + static procfile *ff = NULL;
533 + static kernel_uint_t utime_raw = 0, stime_raw = 0, gtime_raw = 0, gntime_raw = 0, ntime_raw = 0;
534 + static usec_t collected_usec = 0, last_collected_usec = 0;
535 +
536 + if(unlikely(!ff)) {
537 + snprintfz(filename, FILENAME_MAX, "%s/proc/stat", netdata_configured_host_prefix);
538 + ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
539 + if(unlikely(!ff)) goto cleanup;
540 + }
541 +
542 + ff = procfile_readall(ff);
543 + if(unlikely(!ff)) goto cleanup;
544 +
545 + last_collected_usec = collected_usec;
546 + collected_usec = now_monotonic_usec();
547 +
548 + calls_counter++;
549 +
550 + // temporary - it is added global_ntime;
551 + kernel_uint_t global_ntime = 0;
552 +
553 + incremental_rate(global_utime, utime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 1)), collected_usec, last_collected_usec, CPU_TO_NANOSECONDCORES);
554 + incremental_rate(global_ntime, ntime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 2)), collected_usec, last_collected_usec, CPU_TO_NANOSECONDCORES);
555 + incremental_rate(global_stime, stime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 3)), collected_usec, last_collected_usec, CPU_TO_NANOSECONDCORES);
556 + incremental_rate(global_gtime, gtime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 10)), collected_usec, last_collected_usec, CPU_TO_NANOSECONDCORES);
557 +
558 + global_utime += global_ntime;
559 +
560 + if(enable_guest_charts) {
561 + // temporary - it is added global_ntime;
562 + kernel_uint_t global_gntime = 0;
563 +
564 + // guest nice time, on guest time
565 + incremental_rate(global_gntime, gntime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 11)), collected_usec, last_collected_usec, 1);
566 +
567 + global_gtime += global_gntime;
568 +
569 + // remove guest time from user time
570 + global_utime -= (global_utime > global_gtime) ? global_gtime : global_utime;
571 + }
572 +
573 + if(unlikely(global_iterations_counter == 1)) {
574 + global_utime = 0;
575 + global_stime = 0;
576 + global_gtime = 0;
577 + }
578 +
579 + return true;
580 +
581 +cleanup:
582 + global_utime = 0;
583 + global_stime = 0;
584 + global_gtime = 0;
585 + return false;
586 +}
587 +
588 +// --------------------------------------------------------------------------------------------------------------------
589 +// /proc/pid/stat
590 +
591 +static inline void update_proc_state_count(char proc_stt) {
592 + switch (proc_stt) {
593 + case 'S':
594 + proc_state_count[PROC_STATUS_SLEEPING] += 1;
595 + break;
596 + case 'R':
597 + proc_state_count[PROC_STATUS_RUNNING] += 1;
598 + break;
599 + case 'D':
600 + proc_state_count[PROC_STATUS_SLEEPING_D] += 1;
601 + break;
602 + case 'Z':
603 + proc_state_count[PROC_STATUS_ZOMBIE] += 1;
604 + break;
605 + case 'T':
606 + proc_state_count[PROC_STATUS_STOPPED] += 1;
607 + break;
608 + default:
609 + break;
610 + }
611 +}
612 +
613 +bool apps_os_read_pid_stat_linux(struct pid_stat *p, void *ptr __maybe_unused) {
614 + static procfile *ff = NULL;
615 +
616 + if(unlikely(!p->stat_filename)) {
617 + char filename[FILENAME_MAX + 1];
618 + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", netdata_configured_host_prefix, p->pid);
619 + p->stat_filename = strdupz(filename);
620 + }
621 +
622 + bool set_quotes = (!ff) ? true : false;
623 +
624 + ff = procfile_reopen(ff, p->stat_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
625 + if(unlikely(!ff)) goto cleanup;
626 +
627 + // if(set_quotes) procfile_set_quotes(ff, "()");
628 + if(unlikely(set_quotes))
629 + procfile_set_open_close(ff, "(", ")");
630 +
631 + ff = procfile_readall(ff);
632 + if(unlikely(!ff)) goto cleanup;
633 +
634 + // p->pid = str2pid_t(procfile_lineword(ff, 0, 0));
635 + char *comm = procfile_lineword(ff, 0, 1);
636 + p->state = *(procfile_lineword(ff, 0, 2));
637 + p->ppid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 3));
638 + // p->pgrp = (int32_t)str2pid_t(procfile_lineword(ff, 0, 4));
639 + // p->session = (int32_t)str2pid_t(procfile_lineword(ff, 0, 5));
640 + // p->tty_nr = (int32_t)str2pid_t(procfile_lineword(ff, 0, 6));
641 + // p->tpgid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 7));
642 + // p->flags = str2uint64_t(procfile_lineword(ff, 0, 8));
643 +
644 + update_pid_comm(p, comm);
645 +
646 + pid_incremental_rate(stat, PDF_MINFLT, str2kernel_uint_t(procfile_lineword(ff, 0, 9)));
647 + pid_incremental_rate(stat, PDF_CMINFLT, str2kernel_uint_t(procfile_lineword(ff, 0, 10)));
648 + pid_incremental_rate(stat, PDF_MAJFLT, str2kernel_uint_t(procfile_lineword(ff, 0, 11)));
649 + pid_incremental_rate(stat, PDF_CMAJFLT, str2kernel_uint_t(procfile_lineword(ff, 0, 12)));
650 + pid_incremental_cpu(stat, PDF_UTIME, str2kernel_uint_t(procfile_lineword(ff, 0, 13)));
651 + pid_incremental_cpu(stat, PDF_STIME, str2kernel_uint_t(procfile_lineword(ff, 0, 14)));
652 + pid_incremental_cpu(stat, PDF_CUTIME, str2kernel_uint_t(procfile_lineword(ff, 0, 15)));
653 + pid_incremental_cpu(stat, PDF_CSTIME, str2kernel_uint_t(procfile_lineword(ff, 0, 16)));
654 + // p->priority = str2kernel_uint_t(procfile_lineword(ff, 0, 17));
655 + // p->nice = str2kernel_uint_t(procfile_lineword(ff, 0, 18));
656 + p->values[PDF_THREADS] = (int32_t) str2uint32_t(procfile_lineword(ff, 0, 19), NULL);
657 + // p->itrealvalue = str2kernel_uint_t(procfile_lineword(ff, 0, 20));
658 + kernel_uint_t collected_starttime = str2kernel_uint_t(procfile_lineword(ff, 0, 21)) / system_hz;
659 + p->values[PDF_UPTIME] = (system_uptime_secs > collected_starttime)?(system_uptime_secs - collected_starttime):0;
660 + // p->vsize = str2kernel_uint_t(procfile_lineword(ff, 0, 22));
661 + // p->rss = str2kernel_uint_t(procfile_lineword(ff, 0, 23));
662 + // p->rsslim = str2kernel_uint_t(procfile_lineword(ff, 0, 24));
663 + // p->starcode = str2kernel_uint_t(procfile_lineword(ff, 0, 25));
664 + // p->endcode = str2kernel_uint_t(procfile_lineword(ff, 0, 26));
665 + // p->startstack = str2kernel_uint_t(procfile_lineword(ff, 0, 27));
666 + // p->kstkesp = str2kernel_uint_t(procfile_lineword(ff, 0, 28));
667 + // p->kstkeip = str2kernel_uint_t(procfile_lineword(ff, 0, 29));
668 + // p->signal = str2kernel_uint_t(procfile_lineword(ff, 0, 30));
669 + // p->blocked = str2kernel_uint_t(procfile_lineword(ff, 0, 31));
670 + // p->sigignore = str2kernel_uint_t(procfile_lineword(ff, 0, 32));
671 + // p->sigcatch = str2kernel_uint_t(procfile_lineword(ff, 0, 33));
672 + // p->wchan = str2kernel_uint_t(procfile_lineword(ff, 0, 34));
673 + // p->nswap = str2kernel_uint_t(procfile_lineword(ff, 0, 35));
674 + // p->cnswap = str2kernel_uint_t(procfile_lineword(ff, 0, 36));
675 + // p->exit_signal = str2kernel_uint_t(procfile_lineword(ff, 0, 37));
676 + // p->processor = str2kernel_uint_t(procfile_lineword(ff, 0, 38));
677 + // p->rt_priority = str2kernel_uint_t(procfile_lineword(ff, 0, 39));
678 + // p->policy = str2kernel_uint_t(procfile_lineword(ff, 0, 40));
679 + // p->delayacct_blkio_ticks = str2kernel_uint_t(procfile_lineword(ff, 0, 41));
680 +
681 + if(enable_guest_charts) {
682 + pid_incremental_cpu(stat, PDF_GTIME, str2kernel_uint_t(procfile_lineword(ff, 0, 42)));
683 + pid_incremental_cpu(stat, PDF_CGTIME, str2kernel_uint_t(procfile_lineword(ff, 0, 43)));
684 +
685 + if (show_guest_time || p->values[PDF_GTIME] || p->values[PDF_CGTIME]) {
686 + p->values[PDF_UTIME] -= (p->values[PDF_UTIME] >= p->values[PDF_GTIME]) ? p->values[PDF_GTIME] : p->values[PDF_UTIME];
687 + p->values[PDF_CUTIME] -= (p->values[PDF_CUTIME] >= p->values[PDF_CGTIME]) ? p->values[PDF_CGTIME] : p->values[PDF_CUTIME];
688 + show_guest_time = true;
689 + }
690 + }
691 +
692 + if(unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
693 + debug_log_int("READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT ", threads=" KERNEL_UINT_FORMAT,
694 + netdata_configured_host_prefix, p->pid, pid_stat_comm(p), (p->target)?string2str(p->target->name):"UNSET", p->stat_collected_usec - p->last_stat_collected_usec,
695 + p->values[PDF_UTIME],
696 + p->values[PDF_STIME],
697 + p->values[PDF_CUTIME],
698 + p->values[PDF_CSTIME],
699 + p->values[PDF_MINFLT],
700 + p->values[PDF_MAJFLT],
701 + p->values[PDF_CMINFLT],
702 + p->values[PDF_CMAJFLT],
703 + p->values[PDF_THREADS]);
704 +
705 + update_proc_state_count(p->state);
706 + return true;
707 +
708 +cleanup:
709 + return false;
710 +}
711 +
712 +// ----------------------------------------------------------------------------
713 +
714 +// 1. read all files in /proc
715 +// 2. for each numeric directory:
716 +// i. read /proc/pid/stat
717 +// ii. read /proc/pid/status
718 +// iii. read /proc/pid/io (requires root access)
719 +// iii. read the entries in directory /proc/pid/fd (requires root access)
720 +// for each entry:
721 +// a. find or create a struct file_descriptor
722 +// b. cleanup any old/unused file_descriptors
723 +
724 +// after all these, some pids may be linked to targets, while others may not
725 +
726 +// in case of errors, only 1 every 1000 errors is printed
727 +// to avoid filling up all disk space
728 +// if debug is enabled, all errors are printed
729 +
730 +bool apps_os_collect_all_pids_linux(void) {
731 +#if (PROCESSES_HAVE_STATE == 1)
732 + // clear process state counter
733 + memset(proc_state_count, 0, sizeof proc_state_count);
734 +#endif
735 +
736 + // preload the parents and then their children
737 + collect_parents_before_children();
738 +
739 + static char uptime_filename[FILENAME_MAX + 1] = "";
740 + if(*uptime_filename == '\0')
741 + snprintfz(uptime_filename, FILENAME_MAX, "%s/proc/uptime", netdata_configured_host_prefix);
742 +
743 + system_uptime_secs = (kernel_uint_t)(uptime_msec(uptime_filename) / MSEC_PER_SEC);
744 +
745 + char dirname[FILENAME_MAX + 1];
746 +
747 + snprintfz(dirname, FILENAME_MAX, "%s/proc", netdata_configured_host_prefix);
748 + DIR *dir = opendir(dirname);
749 + if(!dir) return false;
750 +
751 + struct dirent *de = NULL;
752 +
753 + while((de = readdir(dir))) {
754 + char *endptr = de->d_name;
755 +
756 + if(unlikely(de->d_type != DT_DIR || de->d_name[0] < '0' || de->d_name[0] > '9'))
757 + continue;
758 +
759 + pid_t pid = (pid_t) strtoul(de->d_name, &endptr, 10);
760 +
761 + // make sure we read a valid number
762 + if(unlikely(endptr == de->d_name || *endptr != '\0'))
763 + continue;
764 +
765 + incrementally_collect_data_for_pid(pid, NULL);
766 + }
767 + closedir(dir);
768 +
769 + return true;
770 +}
771 +#endif
src/collectors/apps.plugin/apps_os_macos.c new
+334
@@ -0,0 +1,334 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +#if defined(OS_MACOS)
6 +
7 +usec_t system_current_time_ut;
8 +mach_timebase_info_data_t mach_info;
9 +
10 +void apps_os_init_macos(void) {
11 + mach_timebase_info(&mach_info);
12 +}
13 +
14 +uint64_t apps_os_get_total_memory_macos(void) {
15 + uint64_t ret = 0;
16 + int mib[2] = {CTL_HW, HW_MEMSIZE};
17 + size_t size = sizeof(ret);
18 + if (sysctl(mib, 2, &ret, &size, NULL, 0) == -1) {
19 + netdata_log_error("Failed to get total memory using sysctl");
20 + return 0;
21 + }
22 +
23 + return ret;
24 +}
25 +
26 +bool apps_os_read_pid_fds_macos(struct pid_stat *p, void *ptr __maybe_unused) {
27 + static struct proc_fdinfo *fds = NULL;
28 + static int fdsCapacity = 0;
29 +
30 + int bufferSize = proc_pidinfo(p->pid, PROC_PIDLISTFDS, 0, NULL, 0);
31 + if (bufferSize <= 0) {
32 + netdata_log_error("Failed to get the size of file descriptors for PID %d", p->pid);
33 + return false;
34 + }
35 +
36 + // Resize buffer if necessary
37 + if (bufferSize > fdsCapacity) {
38 + if(fds)
39 + freez(fds);
40 +
41 + fds = mallocz(bufferSize);
42 + fdsCapacity = bufferSize;
43 + }
44 +
45 + int num_fds = proc_pidinfo(p->pid, PROC_PIDLISTFDS, 0, fds, bufferSize) / PROC_PIDLISTFD_SIZE;
46 + if (num_fds <= 0) {
47 + netdata_log_error("Failed to get the file descriptors for PID %d", p->pid);
48 + return false;
49 + }
50 +
51 + for (int i = 0; i < num_fds; i++) {
52 + switch (fds[i].proc_fdtype) {
53 + case PROX_FDTYPE_VNODE: {
54 + struct vnode_fdinfowithpath vi;
55 + if (proc_pidfdinfo(p->pid, fds[i].proc_fd, PROC_PIDFDVNODEPATHINFO, &vi, sizeof(vi)) > 0)
56 + p->openfds.files++;
57 + else
58 + p->openfds.other++;
59 +
60 + break;
61 + }
62 + case PROX_FDTYPE_SOCKET: {
63 + p->openfds.sockets++;
64 + break;
65 + }
66 + case PROX_FDTYPE_PIPE: {
67 + p->openfds.pipes++;
68 + break;
69 + }
70 +
71 + default:
72 + p->openfds.other++;
73 + break;
74 + }
75 + }
76 +
77 + return true;
78 +}
79 +
80 +bool apps_os_get_pid_cmdline_macos(struct pid_stat *p, char *cmdline, size_t maxBytes) {
81 + int mib[3] = {CTL_KERN, KERN_PROCARGS2, p->pid};
82 + static char *args = NULL;
83 + static size_t size = 0;
84 +
85 + size_t new_size;
86 + if (sysctl(mib, 3, NULL, &new_size, NULL, 0) == -1) {
87 + return false;
88 + }
89 +
90 + if (new_size > size) {
91 + if (args)
92 + freez(args);
93 +
94 + args = (char *)mallocz(new_size);
95 + size = new_size;
96 + }
97 +
98 + memset(cmdline, 0, new_size < maxBytes ? new_size : maxBytes);
99 +
100 + size_t used_size = size;
101 + if (sysctl(mib, 3, args, &used_size, NULL, 0) == -1)
102 + return false;
103 +
104 + int argc;
105 + memcpy(&argc, args, sizeof(argc));
106 + char *ptr = args + sizeof(argc);
107 + used_size -= sizeof(argc);
108 +
109 + // Skip the executable path
110 + while (*ptr && used_size > 0) {
111 + ptr++;
112 + used_size--;
113 + }
114 +
115 + // Copy only the arguments to the cmdline buffer, skipping the environment variables
116 + size_t i = 0, copied_args = 0;
117 + bool inArg = false;
118 + for (; used_size > 0 && i < maxBytes - 1 && copied_args < argc; --used_size, ++ptr) {
119 + if (*ptr == '\0') {
120 + if (inArg) {
121 + cmdline[i++] = ' '; // Replace nulls between arguments with spaces
122 + inArg = false;
123 + copied_args++;
124 + }
125 + } else {
126 + cmdline[i++] = *ptr;
127 + inArg = true;
128 + }
129 + }
130 +
131 + if (i > 0 && cmdline[i - 1] == ' ')
132 + i--; // Remove the trailing space if present
133 +
134 + cmdline[i] = '\0'; // Null-terminate the string
135 +
136 + return true;
137 +}
138 +
139 +bool apps_os_read_pid_io_macos(struct pid_stat *p, void *ptr) {
140 + struct pid_info *pi = ptr;
141 +
142 + // On MacOS, the proc_pid_rusage provides disk_io_statistics which includes io bytes read and written
143 + // but does not provide the same level of detail as Linux, like separating logical and physical I/O bytes.
144 + pid_incremental_rate(io, PDF_LREAD, pi->rusageinfo.ri_diskio_bytesread);
145 + pid_incremental_rate(io, PDF_LWRITE, pi->rusageinfo.ri_diskio_byteswritten);
146 +
147 + return true;
148 +}
149 +
150 +bool apps_os_read_pid_limits_macos(struct pid_stat *p __maybe_unused, void *ptr __maybe_unused) {
151 + return false;
152 +}
153 +
154 +bool apps_os_read_pid_status_macos(struct pid_stat *p, void *ptr) {
155 + struct pid_info *pi = ptr;
156 +
157 + p->uid = pi->bsdinfo.pbi_uid;
158 + p->gid = pi->bsdinfo.pbi_gid;
159 + p->values[PDF_VMSIZE] = pi->taskinfo.pti_virtual_size;
160 + p->values[PDF_VMRSS] = pi->taskinfo.pti_resident_size;
161 + // p->values[PDF_VMSWAP] = rusageinfo.ri_swapins + rusageinfo.ri_swapouts; // This is not directly available, consider an alternative representation
162 + p->values[PDF_VOLCTX] = pi->taskinfo.pti_csw;
163 + // p->values[PDF_NVOLCTX] = taskinfo.pti_nivcsw;
164 +
165 + return true;
166 +}
167 +
168 +static inline void get_current_time(void) {
169 + struct timeval current_time;
170 + gettimeofday(&current_time, NULL);
171 + system_current_time_ut = timeval_usec(&current_time);
172 +}
173 +
174 +// bool apps_os_read_global_cpu_utilization_macos(void) {
175 +// static kernel_uint_t utime_raw = 0, stime_raw = 0, ntime_raw = 0;
176 +// static usec_t collected_usec = 0, last_collected_usec = 0;
177 +//
178 +// host_cpu_load_info_data_t cpuinfo;
179 +// mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
180 +//
181 +// if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpuinfo, &count) != KERN_SUCCESS) {
182 +// // Handle error
183 +// goto cleanup;
184 +// }
185 +//
186 +// last_collected_usec = collected_usec;
187 +// collected_usec = now_monotonic_usec();
188 +//
189 +// calls_counter++;
190 +//
191 +// // Convert ticks to time
192 +// // Note: MacOS does not separate nice time from user time in the CPU stats, so you might need to adjust this logic
193 +// kernel_uint_t global_ntime = 0; // Assuming you want to keep track of nice time separately
194 +//
195 +// incremental_rate(global_utime, utime_raw, cpuinfo.cpu_ticks[CPU_STATE_USER] + cpuinfo.cpu_ticks[CPU_STATE_NICE], collected_usec, last_collected_usec, CPU_TO_NANOSECONDCORES);
196 +// incremental_rate(global_ntime, ntime_raw, cpuinfo.cpu_ticks[CPU_STATE_NICE], collected_usec, last_collected_usec, CPU_TO_NANOSECONDCORES);
197 +// incremental_rate(global_stime, stime_raw, cpuinfo.cpu_ticks[CPU_STATE_SYSTEM], collected_usec, last_collected_usec, CPU_TO_NANOSECONDCORES);
198 +//
199 +// global_utime += global_ntime;
200 +//
201 +// if(unlikely(global_iterations_counter == 1)) {
202 +// global_utime = 0;
203 +// global_stime = 0;
204 +// global_gtime = 0;
205 +// }
206 +//
207 +// return 1;
208 +//
209 +// cleanup:
210 +// global_utime = 0;
211 +// global_stime = 0;
212 +// global_gtime = 0;
213 +// return 0;
214 +// }
215 +
216 +bool apps_os_read_pid_stat_macos(struct pid_stat *p, void *ptr) {
217 + struct pid_info *pi = ptr;
218 +
219 + p->ppid = pi->proc.kp_eproc.e_ppid;
220 +
221 + // Update command name and target if changed
222 + char comm[PROC_PIDPATHINFO_MAXSIZE];
223 + int ret = proc_name(p->pid, comm, sizeof(comm));
224 + if (ret <= 0)
225 + strncpyz(comm, "unknown", sizeof(comm) - 1);
226 +
227 + update_pid_comm(p, comm);
228 +
229 + kernel_uint_t userCPU = (pi->taskinfo.pti_total_user * mach_info.numer) / mach_info.denom;
230 + kernel_uint_t systemCPU = (pi->taskinfo.pti_total_system * mach_info.numer) / mach_info.denom;
231 +
232 + // Map the values from taskinfo to the pid_stat structure
233 + pid_incremental_rate(stat, PDF_MINFLT, pi->taskinfo.pti_faults);
234 + pid_incremental_rate(stat, PDF_MAJFLT, pi->taskinfo.pti_pageins);
235 + pid_incremental_cpu(stat, PDF_UTIME, userCPU);
236 + pid_incremental_cpu(stat, PDF_STIME, systemCPU);
237 + p->values[PDF_THREADS] = pi->taskinfo.pti_threadnum;
238 +
239 + usec_t started_ut = timeval_usec(&pi->proc.kp_proc.p_starttime);
240 + p->values[PDF_UPTIME] = (system_current_time_ut > started_ut) ? (system_current_time_ut - started_ut) / USEC_PER_SEC : 0;
241 +
242 + // Note: Some values such as guest time, cutime, cstime, etc., are not directly available in MacOS.
243 + // You might need to approximate or leave them unset depending on your needs.
244 +
245 + if(unlikely(debug_enabled || (p->target && p->target->debug_enabled))) {
246 + debug_log_int("READ PROC/PID/STAT for MacOS: process: '%s' on target '%s' VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", threads=%d",
247 + pid_stat_comm(p), (p->target) ? string2str(p->target->name) : "UNSET",
248 + p->values[PDF_UTIME],
249 + p->values[PDF_STIME],
250 + p->values[PDF_MINFLT],
251 + p->values[PDF_MAJFLT],
252 + p->values[PDF_THREADS]);
253 + }
254 +
255 + // MacOS doesn't have a direct concept of process state like Linux,
256 + // so updating process state count might need a different approach.
257 +
258 + return true;
259 +}
260 +
261 +bool apps_os_collect_all_pids_macos(void) {
262 + // Mark all processes as unread before collecting new data
263 + struct pid_stat *p;
264 + static pid_t *pids = NULL;
265 + static int allocatedProcessCount = 0;
266 +
267 + // Get the number of processes
268 + int numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
269 + if (numberOfProcesses <= 0) {
270 + netdata_log_error("Failed to retrieve the process count");
271 + return false;
272 + }
273 +
274 + // Allocate or reallocate space to hold all the process IDs if necessary
275 + if (numberOfProcesses > allocatedProcessCount) {
276 + // Allocate additional space to avoid frequent reallocations
277 + allocatedProcessCount = numberOfProcesses + 100;
278 + pids = reallocz(pids, allocatedProcessCount * sizeof(pid_t));
279 + }
280 +
281 + // this is required, otherwise the PIDs become totally random
282 + memset(pids, 0, allocatedProcessCount * sizeof(pid_t));
283 +
284 + // get the list of PIDs
285 + numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, pids, allocatedProcessCount * sizeof(pid_t));
286 + if (numberOfProcesses <= 0) {
287 + netdata_log_error("Failed to retrieve the process IDs");
288 + return false;
289 + }
290 +
291 + get_current_time();
292 +
293 + // Collect data for each process
294 + for (int i = 0; i < numberOfProcesses; ++i) {
295 + pid_t pid = pids[i];
296 + if (pid <= 0) continue;
297 +
298 + struct pid_info pi = { 0 };
299 +
300 + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
301 +
302 + size_t procSize = sizeof(pi.proc);
303 + if(sysctl(mib, 4, &pi.proc, &procSize, NULL, 0) == -1) {
304 + netdata_log_error("Failed to get proc for PID %d", pid);
305 + continue;
306 + }
307 + if(procSize == 0) // no such process
308 + continue;
309 +
310 + int st = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pi.taskinfo, sizeof(pi.taskinfo));
311 + if (st <= 0) {
312 + netdata_log_error("Failed to get task info for PID %d", pid);
313 + continue;
314 + }
315 +
316 + st = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &pi.bsdinfo, sizeof(pi.bsdinfo));
317 + if (st <= 0) {
318 + netdata_log_error("Failed to get BSD info for PID %d", pid);
319 + continue;
320 + }
321 +
322 + st = proc_pid_rusage(pid, RUSAGE_INFO_V4, (rusage_info_t *)&pi.rusageinfo);
323 + if (st < 0) {
324 + netdata_log_error("Failed to get resource usage info for PID %d", pid);
325 + continue;
326 + }
327 +
328 + incrementally_collect_data_for_pid(pid, &pi);
329 + }
330 +
331 + return true;
332 +}
333 +
334 +#endif
src/collectors/apps.plugin/apps_os_windows.c new
+831
@@ -0,0 +1,831 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +/*
5 +{
6 + "SystemName": "WIN11",
7 + "NumObjectTypes": 1,
8 + "LittleEndian": 1,
9 + "Version": 1,
10 + "Revision": 1,
11 + "DefaultObject": 238,
12 + "PerfFreq": 10000000,
13 + "PerfTime": 9242655165203,
14 + "PerfTime100nSec": 133716612800215149,
15 + "SystemTime": {
16 + "Year": 2024,
17 + "Month": 9,
18 + "DayOfWeek": 2,
19 + "Day": 24,
20 + "Hour": 14,
21 + "Minute": 21,
22 + "Second": 20,
23 + "Milliseconds": 21
24 + },
25 + "Objects": [
26 + {
27 + "NameId": 230,
28 + "Name": "Process",
29 + "HelpId": 231,
30 + "Help": "The Process performance object consists of counters that monitor running application program and system processes. All the threads in a process share the same address space and have access to the same data.",
31 + "NumInstances": 274,
32 + "NumCounters": 28,
33 + "PerfTime": 133716612800215149,
34 + "PerfFreq": 10000000,
35 + "CodePage": 0,
36 + "DefaultCounter": 0,
37 + "DetailLevel": "Novice (100)",
38 + "Instances": [
39 + {
40 + "Instance": "Idle",
41 + "UniqueID": -1,
42 + "Labels": [
43 + {
44 + "key": "Process",
45 + "value": "Idle"
46 + }
47 + ],
48 + "Counters": [
49 + {
50 + "Counter": "% Processor Time",
51 + "Value": {
52 + "data": 106881107812500,
53 + "time": 133716612800215149,
54 + "type": 542180608,
55 + "multi": 0,
56 + "frequency": 0
57 + },
58 + "Help": "% Processor Time is the percentage of elapsed time that all of process threads used the processor to execution instructions. An instruction is the basic unit of execution in a computer, a thread is the object that executes instructions, and a process is the object created when a program is run. Code executed to handle some hardware interrupts and trap conditions are included in this count.",
59 + "Type": "PERF_100NSEC_TIMER",
60 + "Algorithm": "100 * (data1 - data0) / (time1 - time0)",
61 + "Description": "64-bit Timer in 100 nsec units. Display delta divided by delta time. Display suffix: \"%\""
62 + },
63 + {
64 + "Counter": "% User Time",
65 + "Value": {
66 + "data": 0,
67 + "time": 133716612800215149,
68 + "type": 542180608,
69 + "multi": 0,
70 + "frequency": 0
71 + },
72 + "Help": "% User Time is the percentage of elapsed time that the process threads spent executing code in user mode. Applications, environment subsystems, and integral subsystems execute in user mode. Code executing in user mode cannot damage the integrity of the Windows executive, kernel, and device drivers. Unlike some early operating systems, Windows uses process boundaries for subsystem protection in addition to the traditional protection of user and privileged modes. Some work done by Windows on behalf of the application might appear in other subsystem processes in addition to the privileged time in the process.",
73 + "Type": "PERF_100NSEC_TIMER",
74 + "Algorithm": "100 * (data1 - data0) / (time1 - time0)",
75 + "Description": "64-bit Timer in 100 nsec units. Display delta divided by delta time. Display suffix: \"%\""
76 + },
77 + {
78 + "Counter": "% Privileged Time",
79 + "Value": {
80 + "data": 106881107812500,
81 + "time": 133716612800215149,
82 + "type": 542180608,
83 + "multi": 0,
84 + "frequency": 0
85 + },
86 + "Help": "% Privileged Time is the percentage of elapsed time that the process threads spent executing code in privileged mode. When a Windows system service is called, the service will often run in privileged mode to gain access to system-private data. Such data is protected from access by threads executing in user mode. Calls to the system can be explicit or implicit, such as page faults or interrupts. Unlike some early operating systems, Windows uses process boundaries for subsystem protection in addition to the traditional protection of user and privileged modes. Some work done by Windows on behalf of the application might appear in other subsystem processes in addition to the privileged time in the process.",
87 + "Type": "PERF_100NSEC_TIMER",
88 + "Algorithm": "100 * (data1 - data0) / (time1 - time0)",
89 + "Description": "64-bit Timer in 100 nsec units. Display delta divided by delta time. Display suffix: \"%\""
90 + },
91 + {
92 + "Counter": "Virtual Bytes Peak",
93 + "Value": {
94 + "data": 8192,
95 + "time": 0,
96 + "type": 65792,
97 + "multi": 0,
98 + "frequency": 0
99 + },
100 + "Help": "Virtual Bytes Peak is the maximum size, in bytes, of virtual address space the process has used at any one time. Use of virtual address space does not necessarily imply corresponding use of either disk or main memory pages. However, virtual space is finite, and the process might limit its ability to load libraries.",
101 + "Type": "PERF_COUNTER_LARGE_RAWCOUNT",
102 + "Algorithm": "data0",
103 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
104 + },
105 + {
106 + "Counter": "Virtual Bytes",
107 + "Value": {
108 + "data": 8192,
109 + "time": 0,
110 + "type": 65792,
111 + "multi": 0,
112 + "frequency": 0
113 + },
114 + "Help": "Virtual Bytes is the current size, in bytes, of the virtual address space the process is using. Use of virtual address space does not necessarily imply corresponding use of either disk or main memory pages. Virtual space is finite, and the process can limit its ability to load libraries.",
115 + "Type": "PERF_COUNTER_LARGE_RAWCOUNT",
116 + "Algorithm": "data0",
117 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
118 + },
119 + {
120 + "Counter": "Page Faults/sec",
121 + "Value": {
122 + "data": 9,
123 + "time": 9242655165203,
124 + "type": 272696320,
125 + "multi": 0,
126 + "frequency": 10000000
127 + },
128 + "Help": "Page Faults/sec is the rate at which page faults by the threads executing in this process are occurring. A page fault occurs when a thread refers to a virtual memory page that is not in its working set in main memory. This may not cause the page to be fetched from disk if it is on the standby list and hence already in main memory, or if it is in use by another process with whom the page is shared.",
129 + "Type": "PERF_COUNTER_COUNTER",
130 + "Algorithm": "(data1 - data0) / ((time1 - time0) / frequency)",
131 + "Description": "32-bit Counter. Divide delta by delta time. Display suffix: \"/sec\""
132 + },
133 + {
134 + "Counter": "Working Set Peak",
135 + "Value": {
136 + "data": 8192,
137 + "time": 0,
138 + "type": 65792,
139 + "multi": 0,
140 + "frequency": 0
141 + },
142 + "Help": "Working Set Peak is the maximum size, in bytes, of the Working Set of this process at any point in time. The Working Set is the set of memory pages touched recently by the threads in the process. If free memory in the computer is above a threshold, pages are left in the Working Set of a process even if they are not in use. When free memory falls below a threshold, pages are trimmed from Working Sets. If they are needed they will then be soft-faulted back into the Working Set before they leave main memory.",
143 + "Type": "PERF_COUNTER_LARGE_RAWCOUNT",
144 + "Algorithm": "data0",
145 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
146 + },
147 + {
148 + "Counter": "Working Set",
149 + "Value": {
150 + "data": 8192,
151 + "time": 0,
152 + "type": 65792,
153 + "multi": 0,
154 + "frequency": 0
155 + },
156 + "Help": "Working Set is the current size, in bytes, of the Working Set of this process. The Working Set is the set of memory pages touched recently by the threads in the process. If free memory in the computer is above a threshold, pages are left in the Working Set of a process even if they are not in use. When free memory falls below a threshold, pages are trimmed from Working Sets. If they are needed they will then be soft-faulted back into the Working Set before leaving main memory.",
157 + "Type": "PERF_COUNTER_LARGE_RAWCOUNT",
158 + "Algorithm": "data0",
159 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
160 + },
161 + {
162 + "Counter": "Page File Bytes Peak",
163 + "Value": {
164 + "data": 61440,
165 + "time": 0,
166 + "type": 65792,
167 + "multi": 0,
168 + "frequency": 0
169 + },
170 + "Help": "Page File Bytes Peak is the maximum amount of virtual memory, in bytes, that this process has reserved for use in the paging file(s). Paging files are used to store pages of memory used by the process that are not contained in other files. Paging files are shared by all processes, and the lack of space in paging files can prevent other processes from allocating memory. If there is no paging file, this counter reflects the maximum amount of virtual memory that the process has reserved for use in physical memory.",
171 + "Type": "PERF_COUNTER_LARGE_RAWCOUNT",
172 + "Algorithm": "data0",
173 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
174 + },
175 + {
176 + "Counter": "Page File Bytes",
177 + "Value": {
178 + "data": 61440,
179 + "time": 0,
180 + "type": 65792,
181 + "multi": 0,
182 + "frequency": 0
183 + },
184 + "Help": "Page File Bytes is the current amount of virtual memory, in bytes, that this process has reserved for use in the paging file(s). Paging files are used to store pages of memory used by the process that are not contained in other files. Paging files are shared by all processes, and the lack of space in paging files can prevent other processes from allocating memory. If there is no paging file, this counter reflects the current amount of virtual memory that the process has reserved for use in physical memory.",
185 + "Type": "PERF_COUNTER_LARGE_RAWCOUNT",
186 + "Algorithm": "data0",
187 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
188 + },
189 + {
190 + "Counter": "Private Bytes",
191 + "Value": {
192 + "data": 61440,
193 + "time": 0,
194 + "type": 65792,
195 + "multi": 0,
196 + "frequency": 0
197 + },
198 + "Help": "Private Bytes is the current size, in bytes, of memory that this process has allocated that cannot be shared with other processes.",
199 + "Type": "PERF_COUNTER_LARGE_RAWCOUNT",
200 + "Algorithm": "data0",
201 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
202 + },
203 + {
204 + "Counter": "Thread Count",
205 + "Value": {
206 + "data": 24,
207 + "time": 0,
208 + "type": 65536,
209 + "multi": 0,
210 + "frequency": 0
211 + },
212 + "Help": "The number of threads currently active in this process. An instruction is the basic unit of execution in a processor, and a thread is the object that executes instructions. Every running process has at least one thread.",
213 + "Type": "PERF_COUNTER_RAWCOUNT",
214 + "Algorithm": "data0",
215 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
216 + },
217 + {
218 + "Counter": "Priority Base",
219 + "Value": {
220 + "data": 0,
221 + "time": 0,
222 + "type": 65536,
223 + "multi": 0,
224 + "frequency": 0
225 + },
226 + "Help": "The current base priority of this process. Threads within a process can raise and lower their own base priority relative to the process' base priority.",
227 + "Type": "PERF_COUNTER_RAWCOUNT",
228 + "Algorithm": "data0",
229 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
230 + },
231 + {
232 + "Counter": "Elapsed Time",
233 + "Value": {
234 + "data": 133707369666486855,
235 + "time": 133716612800215149,
236 + "type": 807666944,
237 + "multi": 0,
238 + "frequency": 10000000
239 + },
240 + "Help": "The total elapsed time, in seconds, that this process has been running.",
241 + "Type": "PERF_ELAPSED_TIME",
242 + "Algorithm": "(time0 - data0) / frequency0",
243 + "Description": "The data collected in this counter is actually the start time of the item being measured. For display, this data is subtracted from the sample time to yield the elapsed time as the difference between the two. In the definition below, the PerfTime field of the Object contains the sample time as indicated by the PERF_OBJECT_TIMER bit and the difference is scaled by the PerfFreq of the Object to convert the time units into seconds."
244 + },
245 + {
246 + "Counter": "ID Process",
247 + "Value": {
248 + "data": 0,
249 + "time": 0,
250 + "type": 65536,
251 + "multi": 0,
252 + "frequency": 0
253 + },
254 + "Help": "ID Process is the unique identifier of this process. ID Process numbers are reused, so they only identify a process for the lifetime of that process.",
255 + "Type": "PERF_COUNTER_RAWCOUNT",
256 + "Algorithm": "data0",
257 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
258 + },
259 + {
260 + "Counter": "Creating Process ID",
261 + "Value": {
262 + "data": 0,
263 + "time": 0,
264 + "type": 65536,
265 + "multi": 0,
266 + "frequency": 0
267 + },
268 + "Help": "The Creating Process ID value is the Process ID of the process that created the process. The creating process may have terminated, so this value may no longer identify a running process.",
269 + "Type": "PERF_COUNTER_RAWCOUNT",
270 + "Algorithm": "data0",
271 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
272 + },
273 + {
274 + "Counter": "Pool Paged Bytes",
275 + "Value": {
276 + "data": 0,
277 + "time": 0,
278 + "type": 65536,
279 + "multi": 0,
280 + "frequency": 0
281 + },
282 + "Help": "Pool Paged Bytes is the size, in bytes, of the paged pool, an area of the system virtual memory that is used for objects that can be written to disk when they are not being used. Memory\\\\Pool Paged Bytes is calculated differently than Process\\\\Pool Paged Bytes, so it might not equal Process(_Total)\\\\Pool Paged Bytes. This counter displays the last observed value only; it is not an average.",
283 + "Type": "PERF_COUNTER_RAWCOUNT",
284 + "Algorithm": "data0",
285 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
286 + },
287 + {
288 + "Counter": "Pool Nonpaged Bytes",
289 + "Value": {
290 + "data": 272,
291 + "time": 0,
292 + "type": 65536,
293 + "multi": 0,
294 + "frequency": 0
295 + },
296 + "Help": "Pool Nonpaged Bytes is the size, in bytes, of the nonpaged pool, an area of the system virtual memory that is used for objects that cannot be written to disk, but must remain in physical memory as long as they are allocated. Memory\\\\Pool Nonpaged Bytes is calculated differently than Process\\\\Pool Nonpaged Bytes, so it might not equal Process(_Total)\\\\Pool Nonpaged Bytes. This counter displays the last observed value only; it is not an average.",
297 + "Type": "PERF_COUNTER_RAWCOUNT",
298 + "Algorithm": "data0",
299 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
300 + },
301 + {
302 + "Counter": "Handle Count",
303 + "Value": {
304 + "data": 0,
305 + "time": 0,
306 + "type": 65536,
307 + "multi": 0,
308 + "frequency": 0
309 + },
310 + "Help": "The total number of handles currently open by this process. This number is equal to the sum of the handles currently open by each thread in this process.",
311 + "Type": "PERF_COUNTER_RAWCOUNT",
312 + "Algorithm": "data0",
313 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
314 + },
315 + {
316 + "Counter": "IO Read Operations/sec",
317 + "Value": {
318 + "data": 0,
319 + "time": 9242655165203,
320 + "type": 272696576,
321 + "multi": 0,
322 + "frequency": 10000000
323 + },
324 + "Help": "The rate at which the process is issuing read I/O operations. This counter counts all I/O activity generated by the process to include file, network and device I/Os.",
325 + "Type": "PERF_COUNTER_BULK_COUNT",
326 + "Algorithm": "(data1 - data0) / ((time1 - time0) / frequency)",
327 + "Description": "64-bit Counter. Divide delta by delta time. Display Suffix: \"/sec\""
328 + },
329 + {
330 + "Counter": "IO Write Operations/sec",
331 + "Value": {
332 + "data": 0,
333 + "time": 9242655165203,
334 + "type": 272696576,
335 + "multi": 0,
336 + "frequency": 10000000
337 + },
338 + "Help": "The rate at which the process is issuing write I/O operations. This counter counts all I/O activity generated by the process to include file, network and device I/Os.",
339 + "Type": "PERF_COUNTER_BULK_COUNT",
340 + "Algorithm": "(data1 - data0) / ((time1 - time0) / frequency)",
341 + "Description": "64-bit Counter. Divide delta by delta time. Display Suffix: \"/sec\""
342 + },
343 + {
344 + "Counter": "IO Data Operations/sec",
345 + "Value": {
346 + "data": 0,
347 + "time": 9242655165203,
348 + "type": 272696576,
349 + "multi": 0,
350 + "frequency": 10000000
351 + },
352 + "Help": "The rate at which the process is issuing read and write I/O operations. This counter counts all I/O activity generated by the process to include file, network and device I/Os.",
353 + "Type": "PERF_COUNTER_BULK_COUNT",
354 + "Algorithm": "(data1 - data0) / ((time1 - time0) / frequency)",
355 + "Description": "64-bit Counter. Divide delta by delta time. Display Suffix: \"/sec\""
356 + },
357 + {
358 + "Counter": "IO Other Operations/sec",
359 + "Value": {
360 + "data": 0,
361 + "time": 9242655165203,
362 + "type": 272696576,
363 + "multi": 0,
364 + "frequency": 10000000
365 + },
366 + "Help": "The rate at which the process is issuing I/O operations that are neither read nor write operations (for example, a control function). This counter counts all I/O activity generated by the process to include file, network and device I/Os.",
367 + "Type": "PERF_COUNTER_BULK_COUNT",
368 + "Algorithm": "(data1 - data0) / ((time1 - time0) / frequency)",
369 + "Description": "64-bit Counter. Divide delta by delta time. Display Suffix: \"/sec\""
370 + },
371 + {
372 + "Counter": "IO Read Bytes/sec",
373 + "Value": {
374 + "data": 0,
375 + "time": 9242655165203,
376 + "type": 272696576,
377 + "multi": 0,
378 + "frequency": 10000000
379 + },
380 + "Help": "The rate at which the process is reading bytes from I/O operations. This counter counts all I/O activity generated by the process to include file, network and device I/Os.",
381 + "Type": "PERF_COUNTER_BULK_COUNT",
382 + "Algorithm": "(data1 - data0) / ((time1 - time0) / frequency)",
383 + "Description": "64-bit Counter. Divide delta by delta time. Display Suffix: \"/sec\""
384 + },
385 + {
386 + "Counter": "IO Write Bytes/sec",
387 + "Value": {
388 + "data": 0,
389 + "time": 9242655165203,
390 + "type": 272696576,
391 + "multi": 0,
392 + "frequency": 10000000
393 + },
394 + "Help": "The rate at which the process is writing bytes to I/O operations. This counter counts all I/O activity generated by the process to include file, network and device I/Os.",
395 + "Type": "PERF_COUNTER_BULK_COUNT",
396 + "Algorithm": "(data1 - data0) / ((time1 - time0) / frequency)",
397 + "Description": "64-bit Counter. Divide delta by delta time. Display Suffix: \"/sec\""
398 + },
399 + {
400 + "Counter": "IO Data Bytes/sec",
401 + "Value": {
402 + "data": 0,
403 + "time": 9242655165203,
404 + "type": 272696576,
405 + "multi": 0,
406 + "frequency": 10000000
407 + },
408 + "Help": "The rate at which the process is reading and writing bytes in I/O operations. This counter counts all I/O activity generated by the process to include file, network and device I/Os.",
409 + "Type": "PERF_COUNTER_BULK_COUNT",
410 + "Algorithm": "(data1 - data0) / ((time1 - time0) / frequency)",
411 + "Description": "64-bit Counter. Divide delta by delta time. Display Suffix: \"/sec\""
412 + },
413 + {
414 + "Counter": "IO Other Bytes/sec",
415 + "Value": {
416 + "data": 0,
417 + "time": 9242655165203,
418 + "type": 272696576,
419 + "multi": 0,
420 + "frequency": 10000000
421 + },
422 + "Help": "The rate at which the process is issuing bytes to I/O operations that do not involve data such as control operations. This counter counts all I/O activity generated by the process to include file, network and device I/Os.",
423 + "Type": "PERF_COUNTER_BULK_COUNT",
424 + "Algorithm": "(data1 - data0) / ((time1 - time0) / frequency)",
425 + "Description": "64-bit Counter. Divide delta by delta time. Display Suffix: \"/sec\""
426 + },
427 + {
428 + "Counter": "Working Set - Private",
429 + "Value": {
430 + "data": 8192,
431 + "time": 0,
432 + "type": 65792,
433 + "multi": 0,
434 + "frequency": 0
435 + },
436 + "Help": "Working Set - Private displays the size of the working set, in bytes, that is use for this process only and not shared nor sharable by other processes.",
437 + "Type": "PERF_COUNTER_LARGE_RAWCOUNT",
438 + "Algorithm": "data0",
439 + "Description": "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix."
440 + }
441 + ]
442 + },
443 + */
444 +
445 +
446 +#if defined(OS_WINDOWS)
447 +
448 +#include <tlhelp32.h>
449 +#include <wchar.h>
450 +#include <psapi.h>
451 +#include <tchar.h>
452 +#include <strsafe.h>
453 +
454 +struct perflib_data {
455 + PERF_DATA_BLOCK *pDataBlock;
456 + PERF_OBJECT_TYPE *pObjectType;
457 + PERF_INSTANCE_DEFINITION *pi;
458 + DWORD pid;
459 +};
460 +
461 +BOOL EnableDebugPrivilege() {
462 + HANDLE hToken;
463 + LUID luid;
464 + TOKEN_PRIVILEGES tkp;
465 +
466 + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
467 + return FALSE;
468 +
469 + if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
470 + return FALSE;
471 +
472 + tkp.PrivilegeCount = 1;
473 + tkp.Privileges[0].Luid = luid;
474 + tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
475 +
476 + if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
477 + return FALSE;
478 +
479 + CloseHandle(hToken);
480 +
481 + return TRUE;
482 +}
483 +
484 +void apps_os_init_windows(void) {
485 + PerflibNamesRegistryInitialize();
486 +
487 + if(!EnableDebugPrivilege())
488 + nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to enable debug privilege");
489 +}
490 +
491 +uint64_t apps_os_get_total_memory_windows(void) {
492 + MEMORYSTATUSEX memStat = { 0 };
493 + memStat.dwLength = sizeof(memStat);
494 +
495 + if (!GlobalMemoryStatusEx(&memStat)) {
496 + netdata_log_error("GlobalMemoryStatusEx() failed.");
497 + return 0;
498 + }
499 +
500 + return memStat.ullTotalPhys;
501 +}
502 +
503 +static __thread wchar_t unicode[PATH_MAX];
504 +
505 +// Convert wide string to UTF-8
506 +static STRING *wchar_to_string(WCHAR *s) {
507 + static __thread char utf8[PATH_MAX];
508 + static __thread int utf8_size = sizeof(utf8);
509 +
510 + int len = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
511 + if (len <= 0 || len >= utf8_size)
512 + return NULL;
513 +
514 + WideCharToMultiByte(CP_UTF8, 0, s, -1, utf8, utf8_size, NULL, NULL);
515 + return string_strdupz(utf8);
516 +}
517 +
518 +STRING *GetProcessFriendlyName(WCHAR *path) {
519 + static __thread uint8_t void_buf[1024 * 1024];
520 +
521 + DWORD handle;
522 + DWORD size = GetFileVersionInfoSizeW(path, &handle);
523 + if (size == 0 || size > sizeof(void_buf))
524 + return FALSE;
525 +
526 + if (GetFileVersionInfoW(path, handle, size, void_buf)) {
527 + LPWSTR value = NULL;
528 + UINT len = 0;
529 + DWORD unicode_size = sizeof(unicode) / sizeof(*unicode);
530 + if (VerQueryValueW(void_buf, L"\\StringFileInfo\\040904B0\\FileDescription", (LPVOID*)&value, &len) &&
531 + len > 0 && len < unicode_size) {
532 + wcsncpy(unicode, value, unicode_size - 1);
533 + unicode[unicode_size - 1] = L'\0';
534 + return wchar_to_string(unicode);
535 + }
536 + }
537 +
538 + return NULL;
539 +}
540 +
541 +void GetAllProcessesInfo(void) {
542 + calls_counter++;
543 +
544 + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
545 + if (hSnapshot == INVALID_HANDLE_VALUE) return;
546 +
547 + PROCESSENTRY32W pe32;
548 + pe32.dwSize = sizeof(PROCESSENTRY32W);
549 +
550 + if (!Process32FirstW(hSnapshot, &pe32)) {
551 + CloseHandle(hSnapshot);
552 + return;
553 + }
554 +
555 + do {
556 + struct pid_stat *p = get_or_allocate_pid_entry((pid_t)pe32.th32ProcessID);
557 + p->ppid = (pid_t)pe32.th32ParentProcessID;
558 + if(p->got_info) continue;
559 + p->got_info = true;
560 +
561 + if(!p->initialized) {
562 + string_freez(p->comm);
563 + p->comm = wchar_to_string(pe32.szExeFile);
564 + p->assigned_to_target = false;
565 + }
566 +
567 + HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, p->pid);
568 + if (hProcess == NULL) continue;
569 +
570 + STRING *full_path = NULL;
571 + STRING *friendly_name = NULL;
572 +
573 + DWORD unicode_size = sizeof(unicode) / sizeof(*unicode);
574 + if(QueryFullProcessImageNameW(hProcess, 0, unicode, &unicode_size)) {
575 + full_path = wchar_to_string(unicode);
576 + friendly_name = GetProcessFriendlyName(unicode);
577 + }
578 +
579 + CloseHandle(hProcess);
580 +
581 + if(full_path) {
582 + string_freez(p->cmdline);
583 + p->cmdline = full_path;
584 + }
585 +
586 + if(friendly_name) {
587 + string_freez(p->name);
588 + p->name = friendly_name;
589 + p->assigned_to_target = false;
590 + }
591 + } while (Process32NextW(hSnapshot, &pe32));
592 +
593 + CloseHandle(hSnapshot);
594 +}
595 +
596 +static inline kernel_uint_t perflib_cpu_utilization(COUNTER_DATA *d) {
597 + internal_fatal(d->current.CounterType != PERF_100NSEC_TIMER,
598 + "Wrong timer type");
599 +
600 + ULONGLONG data1 = d->current.Data;
601 + ULONGLONG data0 = d->previous.Data;
602 + LONGLONG time1 = d->current.Time;
603 + LONGLONG time0 = d->previous.Time;
604 +
605 + /*
606 + * The Windows documentation provides the formula for percentage:
607 + *
608 + * 100 * (data1 - data0) / (time1 - time0)
609 + *
610 + * To get a fraction (0.0 to 1.0) instead of a percentage, we
611 + * simply remove the 100 multiplier:
612 + *
613 + * (data1 - data0) / (time1 - time0)
614 + *
615 + * This fraction represents the portion of a single CPU core used
616 + * over the time period. Multiplying this fraction by NSEC_PER_SEC
617 + * converts it to nanosecond-cores:
618 + *
619 + * NSEC_PER_SEC * (data1 - data0) / (time1 - time0)
620 + */
621 +
622 + LONGLONG dt = time1 - time0;
623 + if(dt > 0)
624 + return NSEC_PER_SEC * (data1 - data0) / dt;
625 + else
626 + return 0;
627 +}
628 +
629 +static inline kernel_uint_t perflib_rate(COUNTER_DATA *d) {
630 + ULONGLONG data1 = d->current.Data;
631 + ULONGLONG data0 = d->previous.Data;
632 + LONGLONG time1 = d->current.Time;
633 + LONGLONG time0 = d->previous.Time;
634 +
635 + LONGLONG dt = (time1 - time0);
636 + if(dt > 0)
637 + return (RATES_DETAIL * (data1 - data0)) / dt;
638 + else
639 + return 0;
640 +}
641 +
642 +static inline kernel_uint_t perflib_value(COUNTER_DATA *d) {
643 + internal_fatal(d->current.CounterType != PERF_COUNTER_LARGE_RAWCOUNT &&
644 + d->current.CounterType != PERF_COUNTER_RAWCOUNT,
645 + "Wrong gauge type");
646 +
647 + return d->current.Data;
648 +}
649 +
650 +static inline kernel_uint_t perflib_elapsed(COUNTER_DATA *d) {
651 + ULONGLONG data1 = d->current.Data;
652 + LONGLONG time1 = d->current.Time;
653 + LONGLONG freq1 = d->current.Frequency;
654 +
655 + internal_fatal(d->current.CounterType != PERF_ELAPSED_TIME || !freq1,
656 + "Wrong gauge type");
657 +
658 + return (time1 - data1) / freq1;
659 +}
660 +
661 +bool apps_os_collect_all_pids_windows(void) {
662 + calls_counter++;
663 +
664 + struct perflib_data d = { 0 };
665 + d.pDataBlock = perflibGetPerformanceData(RegistryFindIDByName("Process"));
666 + if(!d.pDataBlock) return false;
667 +
668 + d.pObjectType = perflibFindObjectTypeByName(d.pDataBlock, "Process");
669 + if(!d.pObjectType) {
670 + perflibFreePerformanceData();
671 + return false;
672 + }
673 +
674 + // we need these outside the loop to avoid searching by name all the time
675 + // (our perflib library caches the id inside the COUNTER_DATA).
676 + COUNTER_DATA processId = {.key = "ID Process"};
677 +
678 + d.pi = NULL;
679 + size_t added = 0;
680 + for(LONG i = 0; i < d.pObjectType->NumInstances; i++) {
681 + d.pi = perflibForEachInstance(d.pDataBlock, d.pObjectType, d.pi);
682 + if (!d.pi) break;
683 +
684 + perflibGetInstanceCounter(d.pDataBlock, d.pObjectType, d.pi, &processId);
685 + d.pid = (DWORD) processId.current.Data;
686 + if (d.pid <= 0) continue; // 0 = Idle (this takes all the spare resources)
687 +
688 + // Get or create pid_stat structure
689 + struct pid_stat *p = get_or_allocate_pid_entry((pid_t) d.pid);
690 +
691 + if (unlikely(!p->initialized)) {
692 + // a new pid
693 + p->initialized = true;
694 +
695 + static __thread char name[MAX_PATH];
696 +
697 + if (getInstanceName(d.pDataBlock, d.pObjectType, d.pi, name, sizeof(name))) {
698 + // remove the PID suffix, if any
699 + char pid[UINT64_MAX_LENGTH + 1]; // +1 for the underscore
700 + pid[0] = '_';
701 + print_uint64(&pid[1], p->pid);
702 + size_t pid_len = strlen(pid);
703 + size_t name_len = strlen(name);
704 + if (pid_len < name_len) {
705 + char *compare = &name[name_len - pid_len];
706 + if (strcmp(pid, compare) == 0)
707 + *compare = '\0';
708 + }
709 +
710 + // remove the .exe suffix, if any
711 + name_len = strlen(name);
712 + size_t exe_len = strlen(".exe");
713 + if(exe_len < name_len) {
714 + char *compare = &name[name_len - exe_len];
715 + if (strcmp(".exe", compare) == 0)
716 + *compare = '\0';
717 + }
718 + }
719 + else
720 + strncpyz(name, "unknown", sizeof(name) - 1);
721 +
722 + if(strcmp(name, "wininit") == 0)
723 + INIT_PID = p->pid;
724 +
725 + string_freez(p->comm); // it may be detected in a previous run via GetAllProcessesInfo()
726 + p->comm = string_strdupz(name);
727 + p->got_info = false;
728 + p->assigned_to_target = false;
729 + added++;
730 +
731 + COUNTER_DATA ppid = {.key = "Creating Process ID"};
732 + perflibGetInstanceCounter(d.pDataBlock, d.pObjectType, d.pi, &ppid);
733 + p->ppid = (pid_t) ppid.current.Data;
734 +
735 + p->perflib[PDF_UTIME].key = "% User Time";
736 + p->perflib[PDF_STIME].key = "% Privileged Time";
737 + p->perflib[PDF_VMSIZE].key = "Virtual Bytes";
738 + p->perflib[PDF_VMRSS].key = "Working Set";
739 + p->perflib[PDF_VMSWAP].key = "Page File Bytes";
740 + p->perflib[PDF_LREAD].key = "IO Read Bytes/sec";
741 + p->perflib[PDF_LWRITE].key = "IO Write Bytes/sec";
742 + p->perflib[PDF_OREAD].key = "IO Read Operations/sec";
743 + p->perflib[PDF_OWRITE].key = "IO Write Operations/sec";
744 + p->perflib[PDF_THREADS].key = "Thread Count";
745 + p->perflib[PDF_HANDLES].key = "Handle Count";
746 + p->perflib[PDF_MINFLT].key = "Page Faults/sec";
747 + p->perflib[PDF_UPTIME].key = "Elapsed Time";
748 + }
749 +
750 + pid_collection_started(p);
751 +
752 + // get all data from perflib
753 + size_t ok = 0, failed = 0, invalid = 0;
754 + for (PID_FIELD f = 0; f < PDF_MAX; f++) {
755 + if (p->perflib[f].key) {
756 + if (!perflibGetInstanceCounter(d.pDataBlock, d.pObjectType, d.pi, &p->perflib[f])) {
757 + failed++;
758 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
759 + "Cannot find field '%s' in processes data", p->perflib[f].key);
760 + } else
761 + ok++;
762 + } else
763 + invalid++;
764 + }
765 +
766 + if(failed) {
767 + pid_collection_failed(p);
768 + continue;
769 + }
770 +
771 + // CPU time
772 + p->values[PDF_UTIME] = perflib_cpu_utilization(&p->perflib[PDF_UTIME]);
773 + p->values[PDF_STIME] = perflib_cpu_utilization(&p->perflib[PDF_STIME]);
774 +
775 + // Memory
776 + p->values[PDF_VMRSS] = perflib_value(&p->perflib[PDF_VMRSS]);
777 + p->values[PDF_VMSIZE] = perflib_value(&p->perflib[PDF_VMSIZE]);
778 + p->values[PDF_VMSWAP] = perflib_value(&p->perflib[PDF_VMSWAP]);
779 +
780 + // I/O
781 + p->values[PDF_LREAD] = perflib_rate(&p->perflib[PDF_LREAD]);
782 + p->values[PDF_LWRITE] = perflib_rate(&p->perflib[PDF_LWRITE]);
783 + p->values[PDF_OREAD] = perflib_rate(&p->perflib[PDF_OREAD]);
784 + p->values[PDF_OWRITE] = perflib_rate(&p->perflib[PDF_OWRITE]);
785 +
786 + // Threads
787 + p->values[PDF_THREADS] = perflib_value(&p->perflib[PDF_THREADS]);
788 +
789 + // Handle count
790 + p->values[PDF_HANDLES] = perflib_value(&p->perflib[PDF_HANDLES]);
791 +
792 + // Page faults
793 + // Windows doesn't distinguish between minor and major page faults
794 + p->values[PDF_MINFLT] = perflib_rate(&p->perflib[PDF_MINFLT]);
795 +
796 + // Process uptime
797 + // Convert 100-nanosecond units to seconds
798 + p->values[PDF_UPTIME] = perflib_elapsed(&p->perflib[PDF_UPTIME]);
799 +
800 + pid_collection_completed(p);
801 +
802 +// if(p->perflib[PDF_UTIME].current.Data != p->perflib[PDF_UTIME].previous.Data &&
803 +// p->perflib[PDF_UTIME].current.Data && p->perflib[PDF_UTIME].previous.Data &&
804 +// p->pid == 61812) {
805 +// const char *cmd = string2str(p->comm);
806 +// uint64_t cpu_divisor = NSEC_PER_SEC / 100ULL;
807 +// uint64_t cpus = os_get_system_cpus();
808 +// double u = (double)p->values[PDF_UTIME] / cpu_divisor;
809 +// double s = (double)p->values[PDF_STIME] / cpu_divisor;
810 +// int x = 0;
811 +// x++;
812 +// }
813 + }
814 +
815 + perflibFreePerformanceData();
816 +
817 + if(added) {
818 + GetAllProcessesInfo();
819 +
820 +#if (USE_APPS_GROUPS_CONF == 1)
821 + for(struct pid_stat *p = root_of_pids(); p ;p = p->next) {
822 + if(!p->assigned_to_target)
823 + assign_app_group_target_to_pid(p);
824 + }
825 +#endif
826 + }
827 +
828 + return true;
829 +}
830 +
831 +#endif
src/collectors/apps.plugin/apps_output.c
+168 -104
@@ -76,7 +76,7 @@ void send_resource_usage_to_netdata(usec_t dt) {
76 "SET inode_changes = %zu\n"
77 "SET link_changes = %zu\n"
78 "SET pids = %zu\n"
79 - "SET fds = %d\n"
79 + "SET fds = %"PRIu32"\n"
80 "SET targets = %zu\n"
81 "SET new_pids = %zu\n"
82 "END\n"
@@ -89,7 +89,7 @@ void send_resource_usage_to_netdata(usec_t dt) {
89 , filenames_allocated_counter
90 , inodes_changed_counter
91 , links_changed_counter
92 - , all_pids_count
92 + , all_pids_count()
93 , all_files_len
94 , apps_groups_targets_count
95 , targets_assignment_counter
@@ -103,103 +103,138 @@ void send_collected_data_to_netdata(struct target *root, const char *type, usec_
103 if (unlikely(!w->exposed))
104 continue;
105
106 - send_BEGIN(type, w->clean_name, "processes", dt);
107 - send_SET("processes", w->processes);
106 + send_BEGIN(type, string2str(w->clean_name), "processes", dt);
107 + send_SET("processes", w->values[PDF_PROCESSES]);
108 send_END();
109
110 - send_BEGIN(type, w->clean_name, "threads", dt);
111 - send_SET("threads", w->num_threads);
110 + send_BEGIN(type, string2str(w->clean_name), "threads", dt);
111 + send_SET("threads", w->values[PDF_THREADS]);
112 send_END();
113
114 - if (unlikely(!w->processes && !w->is_other))
114 + if (unlikely(!w->values[PDF_PROCESSES]))
115 continue;
116
117 - send_BEGIN(type, w->clean_name, "cpu_utilization", dt);
118 - send_SET("user", (kernel_uint_t)(w->utime * utime_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cutime * cutime_fix_ratio)) : 0ULL));
119 - send_SET("system", (kernel_uint_t)(w->stime * stime_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cstime * cstime_fix_ratio)) : 0ULL));
117 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME)
118 + send_BEGIN(type, string2str(w->clean_name), "cpu_utilization", dt);
119 + send_SET("user", (kernel_uint_t)(w->values[PDF_UTIME] * utime_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->values[PDF_CUTIME] * cutime_fix_ratio)) : 0ULL));
120 + send_SET("system", (kernel_uint_t)(w->values[PDF_STIME] * stime_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->values[PDF_CSTIME] * cstime_fix_ratio)) : 0ULL));
121 send_END();
122 +#else
123 + send_BEGIN(type, string2str(w->clean_name), "cpu_utilization", dt);
124 + send_SET("user", (kernel_uint_t)(w->values[PDF_UTIME] * utime_fix_ratio));
125 + send_SET("system", (kernel_uint_t)(w->values[PDF_STIME] * stime_fix_ratio));
126 + send_END();
127 +#endif
128
122 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
129 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
130 if (enable_guest_charts) {
124 - send_BEGIN(type, w->clean_name, "cpu_guest_utilization", dt);
125 - send_SET("guest", (kernel_uint_t)(w->gtime * gtime_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cgtime * cgtime_fix_ratio)) : 0ULL));
131 + send_BEGIN(type, string2str(w->clean_name), "cpu_guest_utilization", dt);
132 + send_SET("guest", (kernel_uint_t)(w->values[PDF_GTIME] * gtime_fix_ratio)
133 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
134 + + (include_exited_childs ? ((kernel_uint_t)(w->values[PDF_CGTIME] * cgtime_fix_ratio)) : 0ULL)
135 +#endif
136 + );
137 send_END();
138 }
139 +#endif
140
129 - send_BEGIN(type, w->clean_name, "cpu_context_switches", dt);
130 - send_SET("voluntary", w->status_voluntary_ctxt_switches);
131 - send_SET("involuntary", w->status_nonvoluntary_ctxt_switches);
141 + send_BEGIN(type, string2str(w->clean_name), "mem_private_usage", dt);
142 +#if (PROCESSES_HAVE_VMSHARED == 1)
143 + send_SET("mem", (w->values[PDF_VMRSS] > w->values[PDF_VMSHARED])?(w->values[PDF_VMRSS] - w->values[PDF_VMSHARED]) : 0ULL);
144 +#else
145 + send_SET("mem", w->values[PDF_VMRSS]);
146 +#endif
147 send_END();
148
134 - send_BEGIN(type, w->clean_name, "mem_private_usage", dt);
135 - send_SET("mem", (w->status_vmrss > w->status_vmshared)?(w->status_vmrss - w->status_vmshared) : 0ULL);
149 +#if (PROCESSES_HAVE_VOLCTX == 1) || (PROCESSES_HAVE_NVOLCTX == 1)
150 + send_BEGIN(type, string2str(w->clean_name), "cpu_context_switches", dt);
151 +#if (PROCESSES_HAVE_VOLCTX == 1)
152 + send_SET("voluntary", w->values[PDF_VOLCTX]);
153 +#endif
154 +#if (PROCESSES_HAVE_NVOLCTX == 1)
155 + send_SET("involuntary", w->values[PDF_NVOLCTX]);
156 +#endif
157 send_END();
158 #endif
159
139 - send_BEGIN(type, w->clean_name, "mem_usage", dt);
140 - send_SET("rss", w->status_vmrss);
160 + send_BEGIN(type, string2str(w->clean_name), "mem_usage", dt);
161 + send_SET("rss", w->values[PDF_VMRSS]);
162 send_END();
163
143 -#if !defined(__APPLE__)
144 - send_BEGIN(type, w->clean_name, "vmem_usage", dt);
145 - send_SET("vmem", w->status_vmsize);
164 + send_BEGIN(type, string2str(w->clean_name), "vmem_usage", dt);
165 + send_SET("vmem", w->values[PDF_VMSIZE]);
166 send_END();
147 -#endif
167
149 - send_BEGIN(type, w->clean_name, "mem_page_faults", dt);
150 - send_SET("minor", (kernel_uint_t)(w->minflt * minflt_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cminflt * cminflt_fix_ratio)) : 0ULL));
151 - send_SET("major", (kernel_uint_t)(w->majflt * majflt_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cmajflt * cmajflt_fix_ratio)) : 0ULL));
168 + send_BEGIN(type, string2str(w->clean_name), "mem_page_faults", dt);
169 + send_SET("minor", (kernel_uint_t)(w->values[PDF_MINFLT] * minflt_fix_ratio)
170 +#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
171 + + (include_exited_childs ? ((kernel_uint_t)(w->values[PDF_CMINFLT] * cminflt_fix_ratio)) : 0ULL)
172 +#endif
173 + );
174 +#if (PROCESSES_HAVE_MAJFLT == 1)
175 + send_SET("major", (kernel_uint_t)(w->values[PDF_MAJFLT] * majflt_fix_ratio)
176 +#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
177 + + (include_exited_childs ? ((kernel_uint_t)(w->values[PDF_CMAJFLT] * cmajflt_fix_ratio)) : 0ULL)
178 +#endif
179 + );
180 +#endif
181 send_END();
182
154 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
155 - send_BEGIN(type, w->clean_name, "swap_usage", dt);
156 - send_SET("swap", w->status_vmswap);
183 +#if (PROCESSES_HAVE_VMSWAP == 1)
184 + send_BEGIN(type, string2str(w->clean_name), "swap_usage", dt);
185 + send_SET("swap", w->values[PDF_VMSWAP]);
186 send_END();
187 #endif
188
160 - if (w->processes == 0) {
161 - send_BEGIN(type, w->clean_name, "uptime", dt);
189 + if (w->values[PDF_PROCESSES] == 0) {
190 + send_BEGIN(type, string2str(w->clean_name), "uptime", dt);
191 send_SET("uptime", 0);
192 send_END();
193
194 if (enable_detailed_uptime_charts) {
166 - send_BEGIN(type, w->clean_name, "uptime_summary", dt);
195 + send_BEGIN(type, string2str(w->clean_name), "uptime_summary", dt);
196 send_SET("min", 0);
197 send_SET("avg", 0);
198 send_SET("max", 0);
199 send_END();
200 }
201 } else {
173 - send_BEGIN(type, w->clean_name, "uptime", dt);
202 + send_BEGIN(type, string2str(w->clean_name), "uptime", dt);
203 send_SET("uptime", w->uptime_max);
204 send_END();
205
206 if (enable_detailed_uptime_charts) {
178 - send_BEGIN(type, w->clean_name, "uptime_summary", dt);
207 + send_BEGIN(type, string2str(w->clean_name), "uptime_summary", dt);
208 send_SET("min", w->uptime_min);
180 - send_SET("avg", w->processes > 0 ? w->uptime_sum / w->processes : 0);
209 + send_SET("avg", w->values[PDF_PROCESSES] > 0 ? w->values[PDF_UPTIME] / w->values[PDF_PROCESSES] : 0);
210 send_SET("max", w->uptime_max);
211 send_END();
212 }
213 }
214
186 - send_BEGIN(type, w->clean_name, "disk_physical_io", dt);
187 - send_SET("reads", w->io_storage_bytes_read);
188 - send_SET("writes", w->io_storage_bytes_written);
215 +#if (PROCESSES_HAVE_PHYSICAL_IO == 1)
216 + send_BEGIN(type, string2str(w->clean_name), "disk_physical_io", dt);
217 + send_SET("reads", w->values[PDF_PREAD]);
218 + send_SET("writes", w->values[PDF_PWRITE]);
219 send_END();
220 +#endif
221
191 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
192 - send_BEGIN(type, w->clean_name, "disk_logical_io", dt);
193 - send_SET("reads", w->io_logical_bytes_read);
194 - send_SET("writes", w->io_logical_bytes_written);
222 +#if (PROCESSES_HAVE_LOGICAL_IO == 1)
223 + send_BEGIN(type, string2str(w->clean_name), "disk_logical_io", dt);
224 + send_SET("reads", w->values[PDF_LREAD]);
225 + send_SET("writes", w->values[PDF_LWRITE]);
226 send_END();
227 #endif
228 +
229 if (enable_file_charts) {
198 - send_BEGIN(type, w->clean_name, "fds_open_limit", dt);
230 +#if (PROCESSES_HAVE_FDS == 1)
231 + send_BEGIN(type, string2str(w->clean_name), "fds_open_limit", dt);
232 send_SET("limit", w->max_open_files_percent * 100.0);
233 send_END();
234 +#endif
235
202 - send_BEGIN(type, w->clean_name, "fds_open", dt);
236 + send_BEGIN(type, string2str(w->clean_name), "fds_open", dt);
237 +#if (PROCESSES_HAVE_FDS == 1)
238 send_SET("files", w->openfds.files);
239 send_SET("sockets", w->openfds.sockets);
240 send_SET("pipes", w->openfds.sockets);
@@ -209,6 +244,10 @@ void send_collected_data_to_netdata(struct target *root, const char *type, usec_
244 send_SET("signal", w->openfds.signalfds);
245 send_SET("eventpolls", w->openfds.eventpolls);
246 send_SET("other", w->openfds.other);
247 +#endif
248 +#if (PROCESSES_HAVE_HANDLES == 1)
249 + send_SET("handles", w->values[PDF_HANDLES]);
250 +#endif
251 send_END();
252 }
253 }
@@ -223,9 +262,9 @@ void send_charts_updates_to_netdata(struct target *root, const char *type, const
262
263 if (debug_enabled) {
264 for (w = root; w; w = w->next) {
226 - if (unlikely(!w->target && w->processes)) {
265 + if (unlikely(!w->target && w->values[PDF_PROCESSES])) {
266 struct pid_on_target *pid_on_target;
228 - fprintf(stderr, "apps.plugin: target '%s' has aggregated %u process(es):", w->name, w->processes);
267 + fprintf(stderr, "apps.plugin: target '%s' has aggregated %"PRIu64" process(es):", string2str(w->name), w->values[PDF_PROCESSES]);
268 for (pid_on_target = w->root_pid; pid_on_target; pid_on_target = pid_on_target->next) {
269 fprintf(stderr, " %d", pid_on_target->pid);
270 }
@@ -235,101 +274,120 @@ void send_charts_updates_to_netdata(struct target *root, const char *type, const
274 }
275
276 for (w = root; w; w = w->next) {
238 - if (likely(w->exposed || (!w->processes && !w->is_other)))
277 + if (likely(w->exposed || (!w->values[PDF_PROCESSES])))
278 continue;
279
241 - w->exposed = 1;
280 + w->exposed = true;
281
243 - fprintf(stdout, "CHART %s.%s_cpu_utilization '' '%s CPU utilization (100%% = 1 core)' 'percentage' cpu %s.cpu_utilization stacked 20001 %d\n", type, w->clean_name, title, type, update_every);
244 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
282 + fprintf(stdout, "CHART %s.%s_cpu_utilization '' '%s CPU utilization (100%% = 1 core)' 'percentage' cpu %s.cpu_utilization stacked 20001 %d\n",
283 + type, string2str(w->clean_name), title, type, update_every);
284 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
285 fprintf(stdout, "CLABEL_COMMIT\n");
246 - fprintf(stdout, "DIMENSION user '' absolute 1 %llu\n", time_factor * RATES_DETAIL / 100LLU);
247 - fprintf(stdout, "DIMENSION system '' absolute 1 %llu\n", time_factor * RATES_DETAIL / 100LLU);
286 + fprintf(stdout, "DIMENSION user '' absolute 1 %llu\n", NSEC_PER_SEC / 100ULL);
287 + fprintf(stdout, "DIMENSION system '' absolute 1 %llu\n", NSEC_PER_SEC / 100ULL);
288
249 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
289 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
290 if (enable_guest_charts) {
251 - fprintf(stdout, "CHART %s.%s_cpu_guest_utilization '' '%s CPU guest utlization (100%% = 1 core)' 'percentage' cpu %s.cpu_guest_utilization line 20005 %d\n", type, w->clean_name, title, type, update_every);
252 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
291 + fprintf(stdout, "CHART %s.%s_cpu_guest_utilization '' '%s CPU guest utlization (100%% = 1 core)' 'percentage' cpu %s.cpu_guest_utilization line 20005 %d\n",
292 + type, string2str(w->clean_name), title, type, update_every);
293 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
294 fprintf(stdout, "CLABEL_COMMIT\n");
254 - fprintf(stdout, "DIMENSION guest '' absolute 1 %llu\n", time_factor * RATES_DETAIL / 100LLU);
295 + fprintf(stdout, "DIMENSION guest '' absolute 1 %llu\n", NSEC_PER_SEC / 100ULL);
296 }
297 +#endif
298
257 - fprintf(stdout, "CHART %s.%s_cpu_context_switches '' '%s CPU context switches' 'switches/s' cpu %s.cpu_context_switches stacked 20010 %d\n", type, w->clean_name, title, type, update_every);
258 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
299 + fprintf(stdout, "CHART %s.%s_mem_private_usage '' '%s memory usage without shared' 'MiB' mem %s.mem_private_usage area 20050 %d\n",
300 + type, string2str(w->clean_name), title, type, update_every);
301 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
302 fprintf(stdout, "CLABEL_COMMIT\n");
260 - fprintf(stdout, "DIMENSION voluntary '' absolute 1 %llu\n", RATES_DETAIL);
261 - fprintf(stdout, "DIMENSION involuntary '' absolute 1 %llu\n", RATES_DETAIL);
303 + fprintf(stdout, "DIMENSION mem '' absolute %ld %ld\n", 1L, 1024L * 1024L);
304
263 - fprintf(stdout, "CHART %s.%s_mem_private_usage '' '%s memory usage without shared' 'MiB' mem %s.mem_private_usage area 20050 %d\n", type, w->clean_name, title, type, update_every);
264 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
305 +#if (PROCESSES_HAVE_VOLCTX == 1) || (PROCESSES_HAVE_NVOLCTX == 1)
306 + fprintf(stdout, "CHART %s.%s_cpu_context_switches '' '%s CPU context switches' 'switches/s' cpu %s.cpu_context_switches stacked 20010 %d\n",
307 + type, string2str(w->clean_name), title, type, update_every);
308 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
309 fprintf(stdout, "CLABEL_COMMIT\n");
266 - fprintf(stdout, "DIMENSION mem '' absolute %ld %ld\n", 1L, 1024L);
310 +#if (PROCESSES_HAVE_VOLCTX == 1)
311 + fprintf(stdout, "DIMENSION voluntary '' absolute 1 %llu\n", RATES_DETAIL);
312 +#endif
313 +#if (PROCESSES_HAVE_NVOLCTX == 1)
314 + fprintf(stdout, "DIMENSION involuntary '' absolute 1 %llu\n", RATES_DETAIL);
315 +#endif
316 #endif
317
269 - fprintf(stdout, "CHART %s.%s_mem_usage '' '%s memory RSS usage' 'MiB' mem %s.mem_usage area 20055 %d\n", type, w->clean_name, title, type, update_every);
270 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
318 + fprintf(stdout, "CHART %s.%s_mem_usage '' '%s memory RSS usage' 'MiB' mem %s.mem_usage area 20055 %d\n",
319 + type, string2str(w->clean_name), title, type, update_every);
320 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
321 fprintf(stdout, "CLABEL_COMMIT\n");
272 - fprintf(stdout, "DIMENSION rss '' absolute %ld %ld\n", 1L, 1024L);
322 + fprintf(stdout, "DIMENSION rss '' absolute %ld %ld\n", 1L, 1024L * 1024L);
323
274 -#if !defined(__APPLE__)
275 - fprintf(stdout, "CHART %s.%s_vmem_usage '' '%s virtual memory size' 'MiB' mem %s.vmem_usage line 20065 %d\n", type, w->clean_name, title, type, update_every);
276 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
324 + fprintf(stdout, "CHART %s.%s_vmem_usage '' '%s virtual memory size' 'MiB' mem %s.vmem_usage line 20065 %d\n",
325 + type, string2str(w->clean_name), title, type, update_every);
326 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
327 fprintf(stdout, "CLABEL_COMMIT\n");
278 - fprintf(stdout, "DIMENSION vmem '' absolute %ld %ld\n", 1L, 1024L);
279 -#endif
328 + fprintf(stdout, "DIMENSION vmem '' absolute %ld %ld\n", 1L, 1024L * 1024L);
329
281 - fprintf(stdout, "CHART %s.%s_mem_page_faults '' '%s memory page faults' 'pgfaults/s' mem %s.mem_page_faults stacked 20060 %d\n", type, w->clean_name, title, type, update_every);
282 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
330 + fprintf(stdout, "CHART %s.%s_mem_page_faults '' '%s memory page faults' 'pgfaults/s' mem %s.mem_page_faults stacked 20060 %d\n",
331 + type, string2str(w->clean_name), title, type, update_every);
332 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
333 fprintf(stdout, "CLABEL_COMMIT\n");
284 - fprintf(stdout, "DIMENSION major '' absolute 1 %llu\n", RATES_DETAIL);
334 fprintf(stdout, "DIMENSION minor '' absolute 1 %llu\n", RATES_DETAIL);
335 +#if (PROCESSES_HAVE_MAJFLT == 1)
336 + fprintf(stdout, "DIMENSION major '' absolute 1 %llu\n", RATES_DETAIL);
337 +#endif
338
287 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
288 - fprintf(stdout, "CHART %s.%s_swap_usage '' '%s swap usage' 'MiB' mem %s.swap_usage area 20065 %d\n", type, w->clean_name, title, type, update_every);
289 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
339 +#if (PROCESSES_HAVE_VMSWAP == 1)
340 + fprintf(stdout, "CHART %s.%s_swap_usage '' '%s swap usage' 'MiB' mem %s.swap_usage area 20065 %d\n",
341 + type, string2str(w->clean_name), title, type, update_every);
342 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
343 fprintf(stdout, "CLABEL_COMMIT\n");
291 - fprintf(stdout, "DIMENSION swap '' absolute %ld %ld\n", 1L, 1024L);
344 + fprintf(stdout, "DIMENSION swap '' absolute %ld %ld\n", 1L, 1024L * 1024L);
345 #endif
346
294 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
295 - fprintf(stdout, "CHART %s.%s_disk_physical_io '' '%s disk physical IO' 'KiB/s' disk %s.disk_physical_io area 20100 %d\n", type, w->clean_name, title, type, update_every);
296 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
347 +#if (PROCESSES_HAVE_PHYSICAL_IO == 1)
348 + fprintf(stdout, "CHART %s.%s_disk_physical_io '' '%s disk physical IO' 'KiB/s' disk %s.disk_physical_io area 20100 %d\n",
349 + type, string2str(w->clean_name), title, type, update_every);
350 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
351 fprintf(stdout, "CLABEL_COMMIT\n");
352 fprintf(stdout, "DIMENSION reads '' absolute 1 %llu\n", 1024LLU * RATES_DETAIL);
353 fprintf(stdout, "DIMENSION writes '' absolute -1 %llu\n", 1024LLU * RATES_DETAIL);
354 +#endif
355
301 - fprintf(stdout, "CHART %s.%s_disk_logical_io '' '%s disk logical IO' 'KiB/s' disk %s.disk_logical_io area 20105 %d\n", type, w->clean_name, title, type, update_every);
302 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
356 +#if (PROCESSES_HAVE_LOGICAL_IO == 1)
357 + fprintf(stdout, "CHART %s.%s_disk_logical_io '' '%s disk logical IO' 'KiB/s' disk %s.disk_logical_io area 20105 %d\n",
358 + type, string2str(w->clean_name), title, type, update_every);
359 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
360 fprintf(stdout, "CLABEL_COMMIT\n");
361 fprintf(stdout, "DIMENSION reads '' absolute 1 %llu\n", 1024LLU * RATES_DETAIL);
362 fprintf(stdout, "DIMENSION writes '' absolute -1 %llu\n", 1024LLU * RATES_DETAIL);
306 -#else
307 - fprintf(stdout, "CHART %s.%s_disk_physical_io '' '%s disk physical IO' 'blocks/s' disk %s.disk_physical_block_io area 20100 %d\n", type, w->clean_name, title, type, update_every);
308 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
309 - fprintf(stdout, "CLABEL_COMMIT\n");
310 - fprintf(stdout, "DIMENSION reads '' absolute 1 %llu\n", RATES_DETAIL);
311 - fprintf(stdout, "DIMENSION writes '' absolute -1 %llu\n", RATES_DETAIL);
363 #endif
364
314 - fprintf(stdout, "CHART %s.%s_processes '' '%s processes' 'processes' processes %s.processes line 20150 %d\n", type, w->clean_name, title, type, update_every);
315 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
365 + fprintf(stdout, "CHART %s.%s_processes '' '%s processes' 'processes' processes %s.processes line 20150 %d\n",
366 + type, string2str(w->clean_name), title, type, update_every);
367 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
368 fprintf(stdout, "CLABEL_COMMIT\n");
369 fprintf(stdout, "DIMENSION processes '' absolute 1 1\n");
370
319 - fprintf(stdout, "CHART %s.%s_threads '' '%s threads' 'threads' processes %s.threads line 20155 %d\n", type, w->clean_name, title, type, update_every);
320 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
371 + fprintf(stdout, "CHART %s.%s_threads '' '%s threads' 'threads' processes %s.threads line 20155 %d\n",
372 + type, string2str(w->clean_name), title, type, update_every);
373 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
374 fprintf(stdout, "CLABEL_COMMIT\n");
375 fprintf(stdout, "DIMENSION threads '' absolute 1 1\n");
376
377 if (enable_file_charts) {
325 - fprintf(stdout, "CHART %s.%s_fds_open_limit '' '%s open file descriptors limit' '%%' fds %s.fds_open_limit line 20200 %d\n", type, w->clean_name, title, type, update_every);
326 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
378 +#if (PROCESSES_HAVE_FDS == 1)
379 + fprintf(stdout, "CHART %s.%s_fds_open_limit '' '%s open file descriptors limit' '%%' fds %s.fds_open_limit line 20200 %d\n",
380 + type, string2str(w->clean_name), title, type, update_every);
381 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
382 fprintf(stdout, "CLABEL_COMMIT\n");
383 fprintf(stdout, "DIMENSION limit '' absolute 1 100\n");
384 +#endif
385
330 - fprintf(stdout, "CHART %s.%s_fds_open '' '%s open files descriptors' 'fds' fds %s.fds_open stacked 20210 %d\n", type, w->clean_name, title, type, update_every);
331 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
386 + fprintf(stdout, "CHART %s.%s_fds_open '' '%s open files descriptors' 'fds' fds %s.fds_open stacked 20210 %d\n",
387 + type, string2str(w->clean_name), title, type, update_every);
388 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
389 fprintf(stdout, "CLABEL_COMMIT\n");
390 +#if (PROCESSES_HAVE_FDS == 1)
391 fprintf(stdout, "DIMENSION files '' absolute 1 1\n");
392 fprintf(stdout, "DIMENSION sockets '' absolute 1 1\n");
393 fprintf(stdout, "DIMENSION pipes '' absolute 1 1\n");
@@ -339,16 +397,22 @@ void send_charts_updates_to_netdata(struct target *root, const char *type, const
397 fprintf(stdout, "DIMENSION signal '' absolute 1 1\n");
398 fprintf(stdout, "DIMENSION eventpolls '' absolute 1 1\n");
399 fprintf(stdout, "DIMENSION other '' absolute 1 1\n");
400 +#endif
401 +#if (PROCESSES_HAVE_HANDLES == 1)
402 + fprintf(stdout, "DIMENSION handles '' absolute 1 1\n");
403 +#endif
404 }
405
344 - fprintf(stdout, "CHART %s.%s_uptime '' '%s uptime' 'seconds' uptime %s.uptime line 20250 %d\n", type, w->clean_name, title, type, update_every);
345 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
406 + fprintf(stdout, "CHART %s.%s_uptime '' '%s uptime' 'seconds' uptime %s.uptime line 20250 %d\n",
407 + type, string2str(w->clean_name), title, type, update_every);
408 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
409 fprintf(stdout, "CLABEL_COMMIT\n");
410 fprintf(stdout, "DIMENSION uptime '' absolute 1 1\n");
411
412 if (enable_detailed_uptime_charts) {
350 - fprintf(stdout, "CHART %s.%s_uptime_summary '' '%s uptime summary' 'seconds' uptime %s.uptime_summary area 20255 %d\n", type, w->clean_name, title, type, update_every);
351 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
413 + fprintf(stdout, "CHART %s.%s_uptime_summary '' '%s uptime summary' 'seconds' uptime %s.uptime_summary area 20255 %d\n",
414 + type, string2str(w->clean_name), title, type, update_every);
415 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, string2str(w->name));
416 fprintf(stdout, "CLABEL_COMMIT\n");
417 fprintf(stdout, "DIMENSION min '' absolute 1 1\n");
418 fprintf(stdout, "DIMENSION avg '' absolute 1 1\n");
@@ -357,8 +421,8 @@ void send_charts_updates_to_netdata(struct target *root, const char *type, const
421 }
422 }
423
424 +#if (PROCESSES_HAVE_STATE == 1)
425 void send_proc_states_count(usec_t dt __maybe_unused) {
361 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
426 static bool chart_added = false;
427 // create chart for count of processes in different states
428 if (!chart_added) {
@@ -379,6 +443,6 @@ void send_proc_states_count(usec_t dt __maybe_unused) {
443 send_SET(proc_states[i], proc_state_count[i]);
444 }
445 send_END();
382 -#endif
446 }
447 +#endif
448
src/collectors/apps.plugin/apps_pid.c new
+759
@@ -0,0 +1,759 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +static inline void link_pid_to_its_parent(struct pid_stat *p);
6 +
7 +// --------------------------------------------------------------------------------------------------------------------
8 +// The index of all pids
9 +
10 +#define SIMPLE_HASHTABLE_NAME _PID
11 +#define SIMPLE_HASHTABLE_VALUE_TYPE struct pid_stat
12 +#define SIMPLE_HASHTABLE_KEY_TYPE int32_t
13 +#define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION pid_stat_to_pid_ptr
14 +#define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION pid_ptr_eq
15 +#define SIMPLE_HASHTABLE_SAMPLE_IMPLEMENTATION 0
16 +#include "libnetdata/simple_hashtable.h"
17 +
18 +static inline int32_t *pid_stat_to_pid_ptr(struct pid_stat *p) {
19 + return &p->pid;
20 +}
21 +
22 +static inline bool pid_ptr_eq(int32_t *a, int32_t *b) {
23 + return *a == *b;
24 +}
25 +
26 +struct {
27 +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
28 + // Another pre-allocated list of all possible pids.
29 + // We need it to assign them a unique sortlist id, so that we
30 + // read parents before children. This is needed to prevent a situation where
31 + // a child is found running, but until we read its parent, it has exited and
32 + // its parent has accumulated its resources.
33 + struct {
34 + size_t size;
35 + struct pid_stat **array;
36 + } sorted;
37 +#endif
38 +
39 + struct {
40 + size_t count; // the number of processes running
41 + struct pid_stat *root;
42 + SIMPLE_HASHTABLE_PID ht;
43 + ARAL *aral;
44 + } all_pids;
45 +} pids = { 0 };
46 +
47 +struct pid_stat *root_of_pids(void) {
48 + return pids.all_pids.root;
49 +}
50 +
51 +size_t all_pids_count(void) {
52 + return pids.all_pids.count;
53 +}
54 +
55 +void apps_pids_init(void) {
56 + pids.all_pids.aral = aral_create("pid_stat", sizeof(struct pid_stat), 1, 65536, NULL, NULL, NULL, false, true);
57 + simple_hashtable_init_PID(&pids.all_pids.ht, 1024);
58 +}
59 +
60 +static inline uint64_t pid_hash(pid_t pid) {
61 + return ((uint64_t)pid << 31) + (uint64_t)pid; // we remove 1 bit when shifting to make it different
62 +}
63 +
64 +inline struct pid_stat *find_pid_entry(pid_t pid) {
65 + if(pid < INIT_PID) return NULL;
66 +
67 + uint64_t hash = pid_hash(pid);
68 + int32_t key = pid;
69 + SIMPLE_HASHTABLE_SLOT_PID *sl = simple_hashtable_get_slot_PID(&pids.all_pids.ht, hash, &key, true);
70 + return(SIMPLE_HASHTABLE_SLOT_DATA(sl));
71 +}
72 +
73 +struct pid_stat *get_or_allocate_pid_entry(pid_t pid) {
74 + uint64_t hash = pid_hash(pid);
75 + int32_t key = pid;
76 + SIMPLE_HASHTABLE_SLOT_PID *sl = simple_hashtable_get_slot_PID(&pids.all_pids.ht, hash, &key, true);
77 + struct pid_stat *p = SIMPLE_HASHTABLE_SLOT_DATA(sl);
78 + if(likely(p))
79 + return p;
80 +
81 + p = aral_callocz(pids.all_pids.aral);
82 +
83 +#if (PROCESSES_HAVE_FDS == 1)
84 + p->fds = mallocz(sizeof(struct pid_fd) * MAX_SPARE_FDS);
85 + p->fds_size = MAX_SPARE_FDS;
86 + init_pid_fds(p, 0, p->fds_size);
87 +#endif
88 +
89 + p->pid = pid;
90 + p->values[PDF_PROCESSES] = 1;
91 +
92 + DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(pids.all_pids.root, p, prev, next);
93 + simple_hashtable_set_slot_PID(&pids.all_pids.ht, sl, hash, p);
94 + pids.all_pids.count++;
95 +
96 + return p;
97 +}
98 +
99 +void del_pid_entry(pid_t pid) {
100 + uint64_t hash = pid_hash(pid);
101 + int32_t key = pid;
102 + SIMPLE_HASHTABLE_SLOT_PID *sl = simple_hashtable_get_slot_PID(&pids.all_pids.ht, hash, &key, true);
103 + struct pid_stat *p = SIMPLE_HASHTABLE_SLOT_DATA(sl);
104 +
105 + if(unlikely(!p)) {
106 + netdata_log_error("attempted to free pid %d that is not allocated.", pid);
107 + return;
108 + }
109 +
110 + debug_log("process %d %s exited, deleting it.", pid, pid_stat_comm(p));
111 +
112 + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(pids.all_pids.root, p, prev, next);
113 + simple_hashtable_del_slot_PID(&pids.all_pids.ht, sl);
114 +
115 +#if defined(OS_LINUX)
116 + {
117 + size_t i;
118 + for(i = 0; i < p->fds_size; i++)
119 + if(p->fds[i].filename)
120 + freez(p->fds[i].filename);
121 + }
122 +
123 + arl_free(p->status_arl);
124 +
125 + freez(p->fds_dirname);
126 + freez(p->stat_filename);
127 + freez(p->status_filename);
128 + freez(p->limits_filename);
129 + freez(p->io_filename);
130 + freez(p->cmdline_filename);
131 +#endif
132 +
133 +#if (PROCESSES_HAVE_FDS == 1)
134 + freez(p->fds);
135 +#endif
136 +
137 + string_freez(p->comm);
138 + string_freez(p->cmdline);
139 + aral_freez(pids.all_pids.aral, p);
140 +
141 + pids.all_pids.count--;
142 +}
143 +
144 +// --------------------------------------------------------------------------------------------------------------------
145 +
146 +static __thread pid_t current_pid;
147 +static __thread kernel_uint_t current_pid_values[PDF_MAX];
148 +
149 +void pid_collection_started(struct pid_stat *p) {
150 + fatal_assert(sizeof(current_pid_values) == sizeof(p->values));
151 + current_pid = p->pid;
152 + memcpy(current_pid_values, p->values, sizeof(current_pid_values));
153 + memset(p->values, 0, sizeof(p->values));
154 + p->values[PDF_PROCESSES] = 1;
155 + p->read = true;
156 +}
157 +
158 +void pid_collection_failed(struct pid_stat *p) {
159 + fatal_assert(current_pid == p->pid);
160 + fatal_assert(sizeof(current_pid_values) == sizeof(p->values));
161 + memcpy(p->values, current_pid_values, sizeof(p->values));
162 +}
163 +
164 +void pid_collection_completed(struct pid_stat *p) {
165 + p->updated = true;
166 + p->keep = false;
167 + p->keeploops = 0;
168 +}
169 +
170 +// --------------------------------------------------------------------------------------------------------------------
171 +// preloading of parents before their children
172 +
173 +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
174 +static inline size_t compute_new_sorted_size(size_t old_size, size_t required_size) {
175 + size_t size = (required_size % 1024 == 0) ? required_size : required_size + 1024;
176 + size = (size / 1024) * 1024;
177 +
178 + if(size < old_size * 2)
179 + size = old_size * 2;
180 +
181 + return size;
182 +}
183 +
184 +static int compar_pid_sortlist(const void *a, const void *b) {
185 + const struct pid_stat *p1 = *(struct pid_stat **)a;
186 + const struct pid_stat *p2 = *(struct pid_stat **)b;
187 +
188 + if(p1->sortlist > p2->sortlist)
189 + return -1;
190 + else
191 + return 1;
192 +}
193 +
194 +bool collect_parents_before_children(void) {
195 + if (!pids.all_pids.count) return false;
196 +
197 + if (pids.all_pids.count > pids.sorted.size) {
198 + size_t new_size = compute_new_sorted_size(pids.sorted.size, pids.all_pids.count);
199 + freez(pids.sorted.array);
200 + pids.sorted.array = mallocz(new_size * sizeof(struct pid_stat *));
201 + pids.sorted.size = new_size;
202 + }
203 +
204 + size_t slc = 0;
205 + struct pid_stat *p = NULL;
206 + uint32_t sortlist = 1;
207 + for (p = root_of_pids(); p && slc < pids.sorted.size; p = p->next) {
208 + pids.sorted.array[slc++] = p;
209 +
210 + // assign a sortlist id to all it and its parents
211 + for (struct pid_stat *pp = p; pp ; pp = pp->parent)
212 + pp->sortlist = sortlist++;
213 + }
214 + size_t sorted = slc;
215 +
216 + static bool logged = false;
217 + if (unlikely(p && !logged)) {
218 + nd_log(
219 + NDLS_COLLECTORS,
220 + NDLP_ERR,
221 + "Internal error: I was thinking I had %zu processes in my arrays, but it seems there are more.",
222 + pids.all_pids.count);
223 + logged = true;
224 + }
225 +
226 + if (include_exited_childs && sorted) {
227 + // Read parents before childs
228 + // This is needed to prevent a situation where
229 + // a child is found running, but until we read
230 + // its parent, it has exited and its parent
231 + // has accumulated its resources.
232 +
233 + qsort((void *)pids.sorted.array, sorted, sizeof(struct pid_stat *), compar_pid_sortlist);
234 +
235 + // we forward read all running processes
236 + // incrementally_collect_data_for_pid() is smart enough,
237 + // not to read the same pid twice per iteration
238 + for (slc = 0; slc < sorted; slc++) {
239 + p = pids.sorted.array[slc];
240 + incrementally_collect_data_for_pid_stat(p, NULL);
241 + }
242 + }
243 +
244 + return true;
245 +}
246 +#endif
247 +
248 +// --------------------------------------------------------------------------------------------------------------------
249 +
250 +static void log_parent_loop(struct pid_stat *p) {
251 + CLEAN_BUFFER *wb = buffer_create(0, NULL);
252 + buffer_sprintf(wb, "original pid %d (%s)", p->pid, string2str(p->comm));
253 +
254 + size_t loops = 0;
255 + for(struct pid_stat *t = p->parent; t && loops < 2 ;t = t->parent) {
256 + buffer_sprintf(wb, " => %d (%s)", t->pid, string2str(t->comm));
257 + if(t == p->parent) loops++;
258 + }
259 +
260 + buffer_sprintf(wb, " : broke loop at %d (%s)", p->pid, string2str(p->comm));
261 +
262 + errno_clear();
263 + nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Parents loop detected: %s", buffer_tostring(wb));
264 +}
265 +
266 +static inline bool is_already_a_parent(struct pid_stat *p, struct pid_stat *pp) {
267 + for(struct pid_stat *t = pp; t ;t = t->parent)
268 + if(t == p) return true;
269 +
270 + return false;
271 +}
272 +
273 +static inline void link_pid_to_its_parent(struct pid_stat *p) {
274 + p->parent = NULL;
275 + if(unlikely(!p->ppid))
276 + return;
277 +
278 + if(unlikely(p->ppid == p->pid)) {
279 + nd_log(NDLS_COLLECTORS, NDLP_WARNING,
280 + "Process %d (%s) states parent %d, which is the same PID. Ignoring it.",
281 + p->pid, string2str(p->comm), p->ppid);
282 + p->ppid = 0;
283 + return;
284 + }
285 +
286 + struct pid_stat *pp = find_pid_entry(p->ppid);
287 + if(likely(pp)) {
288 + fatal_assert(pp->pid == p->ppid);
289 +
290 + if(!is_already_a_parent(p, pp)) {
291 + p->parent = pp;
292 + pp->children_count++;
293 + }
294 + else {
295 + p->parent = pp;
296 + log_parent_loop(p);
297 + p->parent = NULL;
298 + p->ppid = 0;
299 + }
300 + }
301 +#if (PPID_SHOULD_BE_RUNNING == 1)
302 + else {
303 + nd_log(NDLS_COLLECTORS, NDLP_WARNING,
304 + "pid %d %s states parent %d, but the later does not exist.",
305 + p->pid, pid_stat_comm(p), p->ppid);
306 + }
307 +#endif
308 +}
309 +
310 +static inline void link_all_processes_to_their_parents(void) {
311 + // link all children to their parents
312 + // and update children count on parents
313 + for(struct pid_stat *p = root_of_pids(); p ; p = p->next)
314 + link_pid_to_its_parent(p);
315 +}
316 +
317 +// --------------------------------------------------------------------------------------------------------------------
318 +
319 +void update_pid_comm(struct pid_stat *p, const char *comm) {
320 + // some process names have ( and ), remove the parenthesis
321 + size_t len = strlen(comm);
322 + char buf[len + 1];
323 + if(comm[0] == '(' && comm[len - 1] == ')') {
324 + memcpy(buf, &comm[1], len - 2);
325 + buf[len - 2] = '\0';
326 + }
327 + else
328 + memcpy(buf, comm, sizeof(buf));
329 +
330 + // check if the comm is changed
331 + if(!p->comm || strcmp(pid_stat_comm(p), buf) != 0) {
332 + // it is changed
333 +
334 + string_freez(p->comm);
335 + p->comm = string_strdupz(buf);
336 +
337 +#if (PROCESSES_HAVE_CMDLINE == 1)
338 + if(likely(proc_pid_cmdline_is_needed))
339 + managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
340 +#endif
341 +
342 + // the process changes comm, we may have to reassign it to
343 + // an apps_groups.conf target.
344 + p->target = NULL;
345 + }
346 +}
347 +
348 +// --------------------------------------------------------------------------------------------------------------------
349 +
350 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1) || (PROCESSES_HAVE_CHILDREN_FLTS == 1)
351 +//static inline int debug_print_process_and_parents(struct pid_stat *p, usec_t time) {
352 +// char *prefix = "\\_ ";
353 +// int indent = 0;
354 +//
355 +// if(p->parent)
356 +// indent = debug_print_process_and_parents(p->parent, p->stat_collected_usec);
357 +// else
358 +// prefix = " > ";
359 +//
360 +// char buffer[indent + 1];
361 +// int i;
362 +//
363 +// for(i = 0; i < indent ;i++) buffer[i] = ' ';
364 +// buffer[i] = '\0';
365 +//
366 +// fprintf(stderr, " %s %s%s (%d %s %"PRIu64""
367 +// , buffer
368 +// , prefix
369 +// , pid_stat_comm(p)
370 +// , p->pid
371 +// , p->updated?"running":"exited"
372 +// , p->stat_collected_usec - time
373 +// );
374 +//
375 +// if(p->values[PDF_UTIME]) fprintf(stderr, " utime=" KERNEL_UINT_FORMAT, p->values[PDF_UTIME]);
376 +// if(p->values[PDF_STIME]) fprintf(stderr, " stime=" KERNEL_UINT_FORMAT, p->values[PDF_STIME]);
377 +//#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
378 +// if(p->values[PDF_GTIME]) fprintf(stderr, " gtime=" KERNEL_UINT_FORMAT, p->values[PDF_GTIME]);
379 +//#endif
380 +//#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
381 +// if(p->values[PDF_CUTIME]) fprintf(stderr, " cutime=" KERNEL_UINT_FORMAT, p->values[PDF_CUTIME]);
382 +// if(p->values[PDF_CSTIME]) fprintf(stderr, " cstime=" KERNEL_UINT_FORMAT, p->values[PDF_CSTIME]);
383 +//#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
384 +// if(p->values[PDF_CGTIME]) fprintf(stderr, " cgtime=" KERNEL_UINT_FORMAT, p->values[PDF_CGTIME]);
385 +//#endif
386 +//#endif
387 +// if(p->values[PDF_MINFLT]) fprintf(stderr, " minflt=" KERNEL_UINT_FORMAT, p->values[PDF_MINFLT]);
388 +//#if (PROCESSES_HAVE_MAJFLT == 1)
389 +// if(p->values[PDF_MAJFLT]) fprintf(stderr, " majflt=" KERNEL_UINT_FORMAT, p->values[PDF_MAJFLT]);
390 +//#endif
391 +//#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
392 +// if(p->values[PDF_CMINFLT]) fprintf(stderr, " cminflt=" KERNEL_UINT_FORMAT, p->values[PDF_CMINFLT]);
393 +// if(p->values[PDF_CMAJFLT]) fprintf(stderr, " cmajflt=" KERNEL_UINT_FORMAT, p->values[PDF_CMAJFLT]);
394 +//#endif
395 +// fprintf(stderr, ")\n");
396 +//
397 +// return indent + 1;
398 +//}
399 +//
400 +//static inline void debug_print_process_tree(struct pid_stat *p, char *msg __maybe_unused) {
401 +// debug_log("%s: process %s (%d, %s) with parents:", msg, pid_stat_comm(p), p->pid, p->updated?"running":"exited");
402 +// debug_print_process_and_parents(p, p->stat_collected_usec);
403 +//}
404 +//
405 +//static inline void debug_find_lost_child(struct pid_stat *pe, kernel_uint_t lost, int type) {
406 +// int found = 0;
407 +// struct pid_stat *p = NULL;
408 +//
409 +// for(p = root_of_pids(); p ; p = p->next) {
410 +// if(p == pe) continue;
411 +//
412 +// switch(type) {
413 +// case 1:
414 +//#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
415 +// if(p->values[PDF_CMINFLT] > lost) {
416 +// fprintf(stderr, " > process %d (%s) could use the lost exited child minflt " KERNEL_UINT_FORMAT " of process %d (%s)\n",
417 +// p->pid, pid_stat_comm(p), lost, pe->pid, pid_stat_comm(pe));
418 +// found++;
419 +// }
420 +//#endif
421 +// break;
422 +//
423 +// case 2:
424 +//#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
425 +// if(p->values[PDF_CMAJFLT] > lost) {
426 +// fprintf(stderr, " > process %d (%s) could use the lost exited child majflt " KERNEL_UINT_FORMAT " of process %d (%s)\n",
427 +// p->pid, pid_stat_comm(p), lost, pe->pid, pid_stat_comm(pe));
428 +// found++;
429 +// }
430 +//#endif
431 +// break;
432 +//
433 +// case 3:
434 +//#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
435 +// if(p->values[PDF_CUTIME] > lost) {
436 +// fprintf(stderr, " > process %d (%s) could use the lost exited child utime " KERNEL_UINT_FORMAT " of process %d (%s)\n",
437 +// p->pid, pid_stat_comm(p), lost, pe->pid, pid_stat_comm(pe));
438 +// found++;
439 +// }
440 +//#endif
441 +// break;
442 +//
443 +// case 4:
444 +//#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
445 +// if(p->values[PDF_CSTIME] > lost) {
446 +// fprintf(stderr, " > process %d (%s) could use the lost exited child stime " KERNEL_UINT_FORMAT " of process %d (%s)\n",
447 +// p->pid, pid_stat_comm(p), lost, pe->pid, pid_stat_comm(pe));
448 +// found++;
449 +// }
450 +//#endif
451 +// break;
452 +//
453 +// case 5:
454 +//#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1) && (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
455 +// if(p->values[PDF_CGTIME] > lost) {
456 +// fprintf(stderr, " > process %d (%s) could use the lost exited child gtime " KERNEL_UINT_FORMAT " of process %d (%s)\n",
457 +// p->pid, pid_stat_comm(p), lost, pe->pid, pid_stat_comm(pe));
458 +// found++;
459 +// }
460 +//#endif
461 +// break;
462 +// }
463 +// }
464 +//
465 +// if(!found) {
466 +// switch(type) {
467 +// case 1:
468 +// fprintf(stderr, " > cannot find any process to use the lost exited child minflt " KERNEL_UINT_FORMAT " of process %d (%s)\n",
469 +// lost, pe->pid, pid_stat_comm(pe));
470 +// break;
471 +//
472 +// case 2:
473 +// fprintf(stderr, " > cannot find any process to use the lost exited child majflt " KERNEL_UINT_FORMAT " of process %d (%s)\n",
474 +// lost, pe->pid, pid_stat_comm(pe));
475 +// break;
476 +//
477 +// case 3:
478 +// fprintf(stderr, " > cannot find any process to use the lost exited child utime " KERNEL_UINT_FORMAT " of process %d (%s)\n",
479 +// lost, pe->pid, pid_stat_comm(pe));
480 +// break;
481 +//
482 +// case 4:
483 +// fprintf(stderr, " > cannot find any process to use the lost exited child stime " KERNEL_UINT_FORMAT " of process %d (%s)\n",
484 +// lost, pe->pid, pid_stat_comm(pe));
485 +// break;
486 +//
487 +// case 5:
488 +// fprintf(stderr, " > cannot find any process to use the lost exited child gtime " KERNEL_UINT_FORMAT " of process %d (%s)\n",
489 +// lost, pe->pid, pid_stat_comm(pe));
490 +// break;
491 +// }
492 +// }
493 +//}
494 +
495 +static inline kernel_uint_t remove_exited_child_from_parent(kernel_uint_t *field, kernel_uint_t *pfield) {
496 + kernel_uint_t absorbed = 0;
497 +
498 + if(*field > *pfield) {
499 + absorbed += *pfield;
500 + *field -= *pfield;
501 + *pfield = 0;
502 + }
503 + else {
504 + absorbed += *field;
505 + *pfield -= *field;
506 + *field = 0;
507 + }
508 +
509 + return absorbed;
510 +}
511 +
512 +static inline void process_exited_pids(void) {
513 + /*
514 + * WHY WE NEED THIS?
515 + *
516 + * When a child process exits in Linux, its accumulated user time (utime) and its children's accumulated
517 + * user time (cutime) are added to the parent's cutime. This means the parent process's cutime reflects
518 + * the total user time spent by its exited children and their descendants
519 + *
520 + * This results in spikes in the charts.
521 + * In this function we remove the exited children resources from the parent's cutime, but only for the
522 + * children we have been monitoring and to the degree we have data for them. Since previously running
523 + * children have already been reported by us, removing them is the right thing to do.
524 + *
525 + */
526 +
527 + for(struct pid_stat *p = root_of_pids(); p ; p = p->next) {
528 + if(p->updated || !p->stat_collected_usec)
529 + continue;
530 +
531 + bool have_work = false;
532 +
533 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
534 + kernel_uint_t utime = (p->raw[PDF_UTIME] + p->raw[PDF_CUTIME]) * CPU_TO_NANOSECONDCORES;
535 + kernel_uint_t stime = (p->raw[PDF_STIME] + p->raw[PDF_CSTIME]) * CPU_TO_NANOSECONDCORES;
536 + if(utime + stime) have_work = true;
537 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
538 + kernel_uint_t gtime = (p->raw[PDF_GTIME] + p->raw[PDF_CGTIME]) * CPU_TO_NANOSECONDCORES;
539 + if(gtime) have_work = true;
540 +#endif
541 +#endif
542 +
543 +#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
544 + kernel_uint_t minflt = (p->raw[PDF_MINFLT] + p->raw[PDF_CMINFLT]) * RATES_DETAIL;
545 + if(minflt) have_work = true;
546 +#if (PROCESSES_HAVE_MAJFLT == 1)
547 + kernel_uint_t majflt = (p->raw[PDF_MAJFLT] + p->raw[PDF_CMAJFLT]) * RATES_DETAIL;
548 + if(majflt) have_work = true;
549 +#endif
550 +#endif
551 +
552 + if(!have_work)
553 + continue;
554 +
555 +// if(unlikely(debug_enabled)) {
556 +// debug_log("Absorb %s (%d %s total resources: utime=" KERNEL_UINT_FORMAT " stime=" KERNEL_UINT_FORMAT " gtime=" KERNEL_UINT_FORMAT " minflt=" KERNEL_UINT_FORMAT " majflt=" KERNEL_UINT_FORMAT ")"
557 +// , pid_stat_comm(p)
558 +// , p->pid
559 +// , p->updated?"running":"exited"
560 +// , utime
561 +// , stime
562 +// , gtime
563 +// , minflt
564 +// , majflt
565 +// );
566 +// debug_print_process_tree(p, "Searching parents");
567 +// }
568 +
569 + for(struct pid_stat *pp = p->parent; pp ; pp = pp->parent) {
570 + if(!pp->updated) continue;
571 +
572 + kernel_uint_t absorbed;
573 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
574 + absorbed = remove_exited_child_from_parent(&utime, &pp->values[PDF_CUTIME]);
575 +// if(unlikely(debug_enabled && absorbed))
576 +// debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " utime (remaining: " KERNEL_UINT_FORMAT ")",
577 +// pid_stat_comm(pp), pp->pid, pp->updated?"running":"exited", absorbed, utime);
578 +
579 + absorbed = remove_exited_child_from_parent(&stime, &pp->values[PDF_CSTIME]);
580 +// if(unlikely(debug_enabled && absorbed))
581 +// debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " stime (remaining: " KERNEL_UINT_FORMAT ")",
582 +// pid_stat_comm(pp), pp->pid, pp->updated?"running":"exited", absorbed, stime);
583 +
584 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
585 + absorbed = remove_exited_child_from_parent(&gtime, &pp->values[PDF_CGTIME]);
586 +// if(unlikely(debug_enabled && absorbed))
587 +// debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " gtime (remaining: " KERNEL_UINT_FORMAT ")",
588 +// pid_stat_comm(pp), pp->pid, pp->updated?"running":"exited", absorbed, gtime);
589 +#endif
590 +#endif
591 +
592 +#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
593 + absorbed = remove_exited_child_from_parent(&minflt, &pp->values[PDF_CMINFLT]);
594 +// if(unlikely(debug_enabled && absorbed))
595 +// debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " minflt (remaining: " KERNEL_UINT_FORMAT ")",
596 +// pid_stat_comm(pp), pp->pid, pp->updated?"running":"exited", absorbed, minflt);
597 +
598 +#if (PROCESSES_HAVE_MAJFLT == 1)
599 + absorbed = remove_exited_child_from_parent(&majflt, &pp->values[PDF_CMAJFLT]);
600 +// if(unlikely(debug_enabled && absorbed))
601 +// debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " majflt (remaining: " KERNEL_UINT_FORMAT ")",
602 +// pid_stat_comm(pp), pp->pid, pp->updated?"running":"exited", absorbed, majflt);
603 +#endif
604 +#endif
605 +
606 + (void)absorbed;
607 + break;
608 + }
609 +
610 +// if(unlikely(debug_enabled)) {
611 +// if(utime) debug_find_lost_child(p, utime, 3);
612 +// if(stime) debug_find_lost_child(p, stime, 4);
613 +// if(gtime) debug_find_lost_child(p, gtime, 5);
614 +// if(minflt) debug_find_lost_child(p, minflt, 1);
615 +// if(majflt) debug_find_lost_child(p, majflt, 2);
616 +// }
617 +
618 +// debug_log(" > remaining resources - KEEP - for another loop: %s (%d %s total resources: utime=" KERNEL_UINT_FORMAT " stime=" KERNEL_UINT_FORMAT " gtime=" KERNEL_UINT_FORMAT " minflt=" KERNEL_UINT_FORMAT " majflt=" KERNEL_UINT_FORMAT ")"
619 +// , pid_stat_comm(p)
620 +// , p->pid
621 +// , p->updated?"running":"exited"
622 +// , utime
623 +// , stime
624 +// , gtime
625 +// , minflt
626 +// , majflt
627 +// );
628 +
629 + bool done = true;
630 +
631 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
632 + p->values[PDF_UTIME] = utime / CPU_TO_NANOSECONDCORES;
633 + p->values[PDF_STIME] = stime / CPU_TO_NANOSECONDCORES;
634 + p->values[PDF_CUTIME] = 0;
635 + p->values[PDF_CSTIME] = 0;
636 + if(utime + stime) done = false;
637 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
638 + p->values[PDF_GTIME] = gtime / CPU_TO_NANOSECONDCORES;
639 + p->values[PDF_CGTIME] = 0;
640 + if(gtime) done = false;
641 +#endif
642 +#endif
643 +
644 +#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
645 + p->values[PDF_MINFLT] = minflt / RATES_DETAIL;
646 + p->values[PDF_CMINFLT] = 0;
647 + if(minflt) done = false;
648 +#if (PROCESSES_HAVE_MAJFLT == 1)
649 + p->values[PDF_MAJFLT] = majflt / RATES_DETAIL;
650 + p->values[PDF_CMAJFLT] = 0;
651 + if(majflt) done = false;
652 +#endif
653 +#endif
654 +
655 + p->keep = !done;
656 +
657 + if(p->keep) {
658 + // we need to keep its exited parents too, to ensure we will have
659 + // the information to reach the running parent at the next iteration
660 + for (struct pid_stat *pp = p->parent; pp; pp = pp->parent) {
661 + if (pp->updated) break;
662 + pp->keep = true;
663 + }
664 + }
665 + }
666 +}
667 +#endif
668 +
669 +// --------------------------------------------------------------------------------------------------------------------
670 +// the main loop for collecting process data
671 +
672 +static inline void clear_pid_rates(struct pid_stat *p) {
673 + p->values[PDF_UTIME] = 0;
674 + p->values[PDF_STIME] = 0;
675 +
676 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
677 + p->values[PDF_GTIME] = 0;
678 +#endif
679 +
680 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
681 + p->values[PDF_CUTIME] = 0;
682 + p->values[PDF_CSTIME] = 0;
683 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
684 + p->values[PDF_CGTIME] = 0;
685 +#endif
686 +#endif
687 +
688 + p->values[PDF_MINFLT] = 0;
689 +#if (PROCESSES_HAVE_MAJFLT == 1)
690 + p->values[PDF_MAJFLT] = 0;
691 +#endif
692 +
693 +#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
694 + p->values[PDF_CMINFLT] = 0;
695 + p->values[PDF_CMAJFLT] = 0;
696 +#endif
697 +
698 +#if (PROCESSES_HAVE_LOGICAL_IO == 1)
699 + p->values[PDF_LREAD] = 0;
700 + p->values[PDF_LWRITE] = 0;
701 +#endif
702 +
703 +#if (PROCESSES_HAVE_PHYSICAL_IO == 1)
704 + p->values[PDF_PREAD] = 0;
705 + p->values[PDF_PWRITE] = 0;
706 +#endif
707 +
708 +#if (PROCESSES_HAVE_IO_CALLS == 1)
709 + p->values[PDF_OREAD] = 0;
710 + p->values[PDF_OWRITE] = 0;
711 +#endif
712 +
713 +#if (PROCESSES_HAVE_VOLCTX == 1)
714 + p->values[PDF_VOLCTX] = 0;
715 +#endif
716 +
717 +#if (PROCESSES_HAVE_NVOLCTX == 1)
718 + p->values[PDF_NVOLCTX] = 0;
719 +#endif
720 +}
721 +
722 +bool collect_data_for_all_pids(void) {
723 + // mark all pids as unread
724 +#if (INCREMENTAL_DATA_COLLECTION == 0)
725 + usec_t now_mon_ut = now_monotonic_usec();
726 +#endif
727 +
728 + for(struct pid_stat *p = root_of_pids(); p ; p = p->next) {
729 + p->read = p->updated = p->merged = false;
730 + p->children_count = 0;
731 +
732 +#if (INCREMENTAL_DATA_COLLECTION == 0)
733 + p->last_stat_collected_usec = p->stat_collected_usec;
734 + p->last_io_collected_usec = p->io_collected_usec;
735 + p->stat_collected_usec = p->io_collected_usec = now_mon_ut;
736 +#endif
737 + }
738 +
739 + // collect data for all pids
740 + if(!OS_FUNCTION(apps_os_collect_all_pids)())
741 + return false;
742 +
743 + // build the process tree
744 + link_all_processes_to_their_parents();
745 +
746 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1) || (PROCESSES_HAVE_CHILDREN_FLTS == 1)
747 + // merge exited pids to their parents
748 + process_exited_pids();
749 +#endif
750 +
751 + // the first iteration needs to be eliminated
752 + // since we are looking for rates
753 + if(unlikely(global_iterations_counter == 1)) {
754 + for(struct pid_stat *p = root_of_pids(); p ; p = p->next)
755 + if(p->read) clear_pid_rates(p);
756 + }
757 +
758 + return true;
759 +}
src/collectors/apps.plugin/apps_pid_files.c new
+430
@@ -0,0 +1,430 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +#if (PROCESSES_HAVE_FDS == 1)
6 +// ----------------------------------------------------------------------------
7 +// file descriptor
8 +//
9 +// this is used to keep a global list of all open files of the system.
10 +// it is needed in order to calculate the unique files processes have open.
11 +
12 +#define FILE_DESCRIPTORS_INCREASE_STEP 100
13 +
14 +// types for struct file_descriptor->type
15 +typedef enum __attribute__((packed)) fd_filetype {
16 + FILETYPE_OTHER,
17 + FILETYPE_FILE,
18 + FILETYPE_PIPE,
19 + FILETYPE_SOCKET,
20 + FILETYPE_INOTIFY,
21 + FILETYPE_EVENTFD,
22 + FILETYPE_EVENTPOLL,
23 + FILETYPE_TIMERFD,
24 + FILETYPE_SIGNALFD
25 +} FD_FILETYPE;
26 +
27 +struct file_descriptor {
28 + avl_t avl;
29 +
30 +#ifdef NETDATA_INTERNAL_CHECKS
31 + uint32_t magic;
32 +#endif /* NETDATA_INTERNAL_CHECKS */
33 +
34 + const char *name;
35 + uint32_t hash;
36 + uint32_t count;
37 + uint32_t pos;
38 + FD_FILETYPE type;
39 +} *all_files = NULL;
40 +
41 +// ----------------------------------------------------------------------------
42 +
43 +static inline void reallocate_target_fds(struct target *w) {
44 + if(unlikely(!w))
45 + return;
46 +
47 + if(unlikely(!w->target_fds || w->target_fds_size < all_files_size)) {
48 + w->target_fds = reallocz(w->target_fds, sizeof(int) * all_files_size);
49 + memset(&w->target_fds[w->target_fds_size], 0, sizeof(int) * (all_files_size - w->target_fds_size));
50 + w->target_fds_size = all_files_size;
51 + }
52 +}
53 +
54 +static void aggregage_fd_type_on_openfds(FD_FILETYPE type, struct openfds *openfds) {
55 + switch(type) {
56 + case FILETYPE_SOCKET:
57 + openfds->sockets++;
58 + break;
59 +
60 + case FILETYPE_FILE:
61 + openfds->files++;
62 + break;
63 +
64 + case FILETYPE_PIPE:
65 + openfds->pipes++;
66 + break;
67 +
68 + case FILETYPE_INOTIFY:
69 + openfds->inotifies++;
70 + break;
71 +
72 + case FILETYPE_EVENTFD:
73 + openfds->eventfds++;
74 + break;
75 +
76 + case FILETYPE_TIMERFD:
77 + openfds->timerfds++;
78 + break;
79 +
80 + case FILETYPE_SIGNALFD:
81 + openfds->signalfds++;
82 + break;
83 +
84 + case FILETYPE_EVENTPOLL:
85 + openfds->eventpolls++;
86 + break;
87 +
88 + case FILETYPE_OTHER:
89 + openfds->other++;
90 + break;
91 + }
92 +}
93 +
94 +static inline void aggregate_fd_on_target(int fd, struct target *w) {
95 + if(unlikely(!w))
96 + return;
97 +
98 + if(unlikely(w->target_fds[fd])) {
99 + // it is already aggregated
100 + // just increase its usage counter
101 + w->target_fds[fd]++;
102 + return;
103 + }
104 +
105 + // increase its usage counter
106 + // so that we will not add it again
107 + w->target_fds[fd]++;
108 +
109 + aggregage_fd_type_on_openfds(all_files[fd].type, &w->openfds);
110 +}
111 +
112 +void aggregate_pid_fds_on_targets(struct pid_stat *p) {
113 +
114 + if(unlikely(!p->updated)) {
115 + // the process is not running
116 + return;
117 + }
118 +
119 + struct target
120 +#if (PROCESSES_HAVE_UID == 1)
121 + *u = p->uid_target,
122 +#endif
123 +#if (PROCESSES_HAVE_GID == 1)
124 + *g = p->gid_target,
125 +#endif
126 + *w = p->target;
127 +
128 + reallocate_target_fds(w);
129 +#if (PROCESSES_HAVE_UID == 1)
130 + reallocate_target_fds(u);
131 +#endif
132 +#if (PROCESSES_HAVE_GID == 1)
133 + reallocate_target_fds(g);
134 +#endif
135 +
136 +#if (PROCESSES_HAVE_FDS == 1)
137 + p->openfds.files = 0;
138 + p->openfds.pipes = 0;
139 + p->openfds.sockets = 0;
140 + p->openfds.inotifies = 0;
141 + p->openfds.eventfds = 0;
142 + p->openfds.timerfds = 0;
143 + p->openfds.signalfds = 0;
144 + p->openfds.eventpolls = 0;
145 + p->openfds.other = 0;
146 +
147 + uint32_t c, size = p->fds_size;
148 + struct pid_fd *fds = p->fds;
149 + for(c = 0; c < size ;c++) {
150 + int fd = fds[c].fd;
151 +
152 + if(likely(fd <= 0 || (uint32_t)fd >= all_files_size))
153 + continue;
154 +
155 + aggregage_fd_type_on_openfds(all_files[fd].type, &p->openfds);
156 +
157 + aggregate_fd_on_target(fd, w);
158 +#if (PROCESSES_HAVE_UID == 1)
159 + aggregate_fd_on_target(fd, u);
160 +#endif
161 +#if (PROCESSES_HAVE_GID == 1)
162 + aggregate_fd_on_target(fd, g);
163 +#endif
164 + }
165 +#endif
166 +}
167 +
168 +// ----------------------------------------------------------------------------
169 +
170 +int file_descriptor_compare(void* a, void* b) {
171 +#ifdef NETDATA_INTERNAL_CHECKS
172 + if(((struct file_descriptor *)a)->magic != 0x0BADCAFE || ((struct file_descriptor *)b)->magic != 0x0BADCAFE)
173 + netdata_log_error("Corrupted index data detected. Please report this.");
174 +#endif /* NETDATA_INTERNAL_CHECKS */
175 +
176 + if(((struct file_descriptor *)a)->hash < ((struct file_descriptor *)b)->hash)
177 + return -1;
178 +
179 + else if(((struct file_descriptor *)a)->hash > ((struct file_descriptor *)b)->hash)
180 + return 1;
181 +
182 + else
183 + return strcmp(((struct file_descriptor *)a)->name, ((struct file_descriptor *)b)->name);
184 +}
185 +
186 +// int file_descriptor_iterator(avl_t *a) { if(a) {}; return 0; }
187 +
188 +avl_tree_type all_files_index = {
189 + NULL,
190 + file_descriptor_compare
191 +};
192 +
193 +static struct file_descriptor *file_descriptor_find(const char *name, uint32_t hash) {
194 + struct file_descriptor tmp;
195 + tmp.hash = (hash)?hash:simple_hash(name);
196 + tmp.name = name;
197 + tmp.count = 0;
198 + tmp.pos = 0;
199 +#ifdef NETDATA_INTERNAL_CHECKS
200 + tmp.magic = 0x0BADCAFE;
201 +#endif /* NETDATA_INTERNAL_CHECKS */
202 +
203 + return (struct file_descriptor *)avl_search(&all_files_index, (avl_t *) &tmp);
204 +}
205 +
206 +#define file_descriptor_add(fd) avl_insert(&all_files_index, (avl_t *)(fd))
207 +#define file_descriptor_remove(fd) avl_remove(&all_files_index, (avl_t *)(fd))
208 +
209 +// ----------------------------------------------------------------------------
210 +
211 +void file_descriptor_not_used(int id) {
212 + if(id > 0 && (uint32_t)id < all_files_size) {
213 +
214 +#ifdef NETDATA_INTERNAL_CHECKS
215 + if(all_files[id].magic != 0x0BADCAFE) {
216 + netdata_log_error("Ignoring request to remove empty file id %d.", id);
217 + return;
218 + }
219 +#endif /* NETDATA_INTERNAL_CHECKS */
220 +
221 + debug_log("decreasing slot %d (count = %d).", id, all_files[id].count);
222 +
223 + if(all_files[id].count > 0) {
224 + all_files[id].count--;
225 +
226 + if(!all_files[id].count) {
227 + debug_log(" >> slot %d is empty.", id);
228 +
229 + if(unlikely(file_descriptor_remove(&all_files[id]) != (void *)&all_files[id]))
230 + netdata_log_error("INTERNAL ERROR: removal of unused fd from index, removed a different fd");
231 +
232 +#ifdef NETDATA_INTERNAL_CHECKS
233 + all_files[id].magic = 0x00000000;
234 +#endif /* NETDATA_INTERNAL_CHECKS */
235 + all_files_len--;
236 + }
237 + }
238 + else
239 + netdata_log_error("Request to decrease counter of fd %d (%s), while the use counter is 0",
240 + id, all_files[id].name);
241 + }
242 + else
243 + netdata_log_error("Request to decrease counter of fd %d, which is outside the array size (1 to %"PRIu32")",
244 + id, all_files_size);
245 +}
246 +
247 +static inline void all_files_grow() {
248 + void *old = all_files;
249 +
250 + // there is no empty slot
251 + all_files = reallocz(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor));
252 +
253 + // if the address changed, we have to rebuild the index
254 + // since all pointers are now invalid
255 +
256 + if(unlikely(old && old != (void *)all_files)) {
257 + all_files_index.root = NULL;
258 + for(uint32_t i = 0; i < all_files_size; i++) {
259 + if(!all_files[i].count) continue;
260 + if(unlikely(file_descriptor_add(&all_files[i]) != (void *)&all_files[i]))
261 + netdata_log_error("INTERNAL ERROR: duplicate indexing of fd during realloc.");
262 + }
263 + }
264 +
265 + // initialize the newly added entries
266 +
267 + for(uint32_t i = all_files_size; i < (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); i++) {
268 + all_files[i].count = 0;
269 + all_files[i].name = NULL;
270 +#ifdef NETDATA_INTERNAL_CHECKS
271 + all_files[i].magic = 0x00000000;
272 +#endif /* NETDATA_INTERNAL_CHECKS */
273 + all_files[i].pos = i;
274 + }
275 +
276 + if(unlikely(!all_files_size)) all_files_len = 1;
277 + all_files_size += FILE_DESCRIPTORS_INCREASE_STEP;
278 +}
279 +
280 +static inline uint32_t file_descriptor_set_on_empty_slot(const char *name, uint32_t hash, FD_FILETYPE type) {
281 + // check we have enough memory to add it
282 + if(!all_files || all_files_len == all_files_size)
283 + all_files_grow();
284 +
285 + debug_log(" >> searching for empty slot.");
286 +
287 + // search for an empty slot
288 +
289 + static int last_pos = 0;
290 + uint32_t i, c;
291 + for(i = 0, c = last_pos ; i < all_files_size ; i++, c++) {
292 + if(c >= all_files_size) c = 0;
293 + if(c == 0) continue;
294 +
295 + if(!all_files[c].count) {
296 + debug_log(" >> Examining slot %d.", c);
297 +
298 +#ifdef NETDATA_INTERNAL_CHECKS
299 + if(all_files[c].magic == 0x0BADCAFE && all_files[c].name && file_descriptor_find(all_files[c].name, all_files[c].hash))
300 + netdata_log_error("fd on position %"PRIu32" is not cleared properly. It still has %s in it.", c, all_files[c].name);
301 +#endif /* NETDATA_INTERNAL_CHECKS */
302 +
303 + debug_log(" >> %s fd position %d for %s (last name: %s)", all_files[c].name?"re-using":"using", c, name, all_files[c].name);
304 +
305 + freez((void *)all_files[c].name);
306 + all_files[c].name = NULL;
307 + last_pos = c;
308 + break;
309 + }
310 + }
311 +
312 + all_files_len++;
313 +
314 + if(i == all_files_size) {
315 + fatal("We should find an empty slot, but there isn't any");
316 + exit(1);
317 + }
318 + // else we have an empty slot in 'c'
319 +
320 + debug_log(" >> updating slot %d.", c);
321 +
322 + all_files[c].name = strdupz(name);
323 + all_files[c].hash = hash;
324 + all_files[c].type = type;
325 + all_files[c].pos = c;
326 + all_files[c].count = 1;
327 +#ifdef NETDATA_INTERNAL_CHECKS
328 + all_files[c].magic = 0x0BADCAFE;
329 +#endif /* NETDATA_INTERNAL_CHECKS */
330 + if(unlikely(file_descriptor_add(&all_files[c]) != (void *)&all_files[c]))
331 + netdata_log_error("INTERNAL ERROR: duplicate indexing of fd.");
332 +
333 + return c;
334 +}
335 +
336 +uint32_t file_descriptor_find_or_add(const char *name, uint32_t hash) {
337 + if(unlikely(!hash))
338 + hash = simple_hash(name);
339 +
340 + debug_log("adding or finding name '%s' with hash %u", name, hash);
341 +
342 + struct file_descriptor *fd = file_descriptor_find(name, hash);
343 + if(fd) {
344 + // found
345 + debug_log(" >> found on slot %d", fd->pos);
346 +
347 + fd->count++;
348 + return fd->pos;
349 + }
350 + // not found
351 +
352 + FD_FILETYPE type;
353 + if(likely(name[0] == '/')) type = FILETYPE_FILE;
354 + else if(likely(strncmp(name, "pipe:", 5) == 0)) type = FILETYPE_PIPE;
355 + else if(likely(strncmp(name, "socket:", 7) == 0)) type = FILETYPE_SOCKET;
356 + else if(likely(strncmp(name, "anon_inode:", 11) == 0)) {
357 + const char *t = &name[11];
358 +
359 + if(strcmp(t, "inotify") == 0) type = FILETYPE_INOTIFY;
360 + else if(strcmp(t, "[eventfd]") == 0) type = FILETYPE_EVENTFD;
361 + else if(strcmp(t, "[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
362 + else if(strcmp(t, "[timerfd]") == 0) type = FILETYPE_TIMERFD;
363 + else if(strcmp(t, "[signalfd]") == 0) type = FILETYPE_SIGNALFD;
364 + else {
365 + debug_log("UNKNOWN anonymous inode: %s", name);
366 + type = FILETYPE_OTHER;
367 + }
368 + }
369 + else if(likely(strcmp(name, "inotify") == 0)) type = FILETYPE_INOTIFY;
370 + else {
371 + debug_log("UNKNOWN linkname: %s", name);
372 + type = FILETYPE_OTHER;
373 + }
374 +
375 + return file_descriptor_set_on_empty_slot(name, hash, type);
376 +}
377 +
378 +void clear_pid_fd(struct pid_fd *pfd) {
379 + pfd->fd = 0;
380 +
381 +#if defined(OS_LINUX)
382 + pfd->link_hash = 0;
383 + pfd->inode = 0;
384 + pfd->cache_iterations_counter = 0;
385 + pfd->cache_iterations_reset = 0;
386 +#endif
387 +}
388 +
389 +void make_all_pid_fds_negative(struct pid_stat *p) {
390 + struct pid_fd *pfd = p->fds, *pfdend = &p->fds[p->fds_size];
391 + while(pfd < pfdend) {
392 + pfd->fd = -(pfd->fd);
393 + pfd++;
394 + }
395 +}
396 +
397 +static inline void cleanup_negative_pid_fds(struct pid_stat *p) {
398 + struct pid_fd *pfd = p->fds, *pfdend = &p->fds[p->fds_size];
399 +
400 + while(pfd < pfdend) {
401 + int fd = pfd->fd;
402 +
403 + if(unlikely(fd < 0)) {
404 + file_descriptor_not_used(-(fd));
405 + clear_pid_fd(pfd);
406 + }
407 +
408 + pfd++;
409 + }
410 +}
411 +
412 +void init_pid_fds(struct pid_stat *p, size_t first, size_t size) {
413 + struct pid_fd *pfd = &p->fds[first], *pfdend = &p->fds[first + size];
414 +
415 + while(pfd < pfdend) {
416 +#if defined(OS_LINUX)
417 + pfd->filename = NULL;
418 +#endif
419 + clear_pid_fd(pfd);
420 + pfd++;
421 + }
422 +}
423 +
424 +int read_pid_file_descriptors(struct pid_stat *p, void *ptr) {
425 + bool ret = OS_FUNCTION(apps_os_read_pid_fds)(p, ptr);
426 + cleanup_negative_pid_fds(p);
427 +
428 + return ret ? 1 : 0;
429 +}
430 +#endif
\ No newline at end of file
src/collectors/apps.plugin/apps_plugin.c
+115 -504
@@ -27,16 +27,16 @@
27 // options
28
29 bool debug_enabled = false;
30 -bool enable_guest_charts = false;
30 +
31 bool enable_detailed_uptime_charts = false;
32 bool enable_users_charts = true;
33 bool enable_groups_charts = true;
34 bool include_exited_childs = true;
35 -bool proc_pid_cmdline_is_needed = false; // true when we need to read /proc/cmdline
35 +bool proc_pid_cmdline_is_needed = true; // true when we need to read /proc/cmdline
36
37 -#if defined(__FreeBSD__) || defined(__APPLE__)
37 +#if defined(OS_FREEBSD) || defined(OS_MACOS) || defined(OS_WINDOWS)
38 bool enable_file_charts = false;
39 -#else
39 +#elif defined(OS_LINUX)
40 bool enable_file_charts = true;
41 #endif
42
@@ -53,19 +53,16 @@ size_t
53 targets_assignment_counter = 0,
54 apps_groups_targets_count = 0; // # of apps_groups.conf targets
55
56 -int
57 - all_files_len = 0,
58 - all_files_size = 0,
59 - show_guest_time = 0, // 1 when guest values are collected
60 - show_guest_time_old = 0;
61 -
62 -#if defined(__FreeBSD__) || defined(__APPLE__)
63 -usec_t system_current_time_ut;
64 -#else
65 -kernel_uint_t system_uptime_secs;
56 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
57 +bool enable_guest_charts = false;
58 +bool show_guest_time = false; // set when guest values are collected
59 #endif
60
68 -// ----------------------------------------------------------------------------
61 +uint32_t
62 + all_files_len = 0,
63 + all_files_size = 0;
64 +
65 +// --------------------------------------------------------------------------------------------------------------------
66 // Normalization
67 //
68 // With normalization we lower the collected metrics by a factor to make them
@@ -80,10 +77,12 @@ kernel_uint_t system_uptime_secs;
77 // metric.
78
79 // the total system time, as reported by /proc/stat
80 +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
81 kernel_uint_t
82 global_utime = 0,
83 global_stime = 0,
84 global_gtime = 0;
85 +#endif
86
87 // the normalization ratios, as calculated by normalize_utilization()
88 NETDATA_DOUBLE
@@ -98,21 +97,11 @@ NETDATA_DOUBLE
97 cminflt_fix_ratio = 1.0,
98 cmajflt_fix_ratio = 1.0;
99
101 -// ----------------------------------------------------------------------------
102 -// factor for calculating correct CPU time values depending on units of raw data
103 -unsigned int time_factor = 0;
104 -
105 -// ----------------------------------------------------------------------------
106 -// command line options
100 +// --------------------------------------------------------------------------------------------------------------------
101
102 int update_every = 1;
103
110 -#if defined(__APPLE__)
111 -mach_timebase_info_data_t mach_info;
112 -#endif
113 -
114 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
115 -int max_fds_cache_seconds = 60;
104 +#if defined(OS_LINUX)
105 proc_state proc_state_count[PROC_STATUS_END];
106 const char *proc_states[] = {
107 [PROC_STATUS_RUNNING] = "running",
@@ -127,412 +116,12 @@ const char *proc_states[] = {
116 static char *user_config_dir = CONFIG_DIR;
117 static char *stock_config_dir = LIBCONFIG_DIR;
118
130 -struct target
131 - *apps_groups_default_target = NULL, // the default target
132 - *apps_groups_root_target = NULL, // apps_groups.conf defined
133 - *users_root_target = NULL, // users
134 - *groups_root_target = NULL; // user groups
135 -
119 size_t pagesize;
120
138 -// ----------------------------------------------------------------------------
139 -
140 -int managed_log(struct pid_stat *p, PID_LOG log, int status) {
141 - if(unlikely(!status)) {
142 - // netdata_log_error("command failed log %u, errno %d", log, errno);
143 -
144 - if(unlikely(debug_enabled || errno != ENOENT)) {
145 - if(unlikely(debug_enabled || !(p->log_thrown & log))) {
146 - p->log_thrown |= log;
147 - switch(log) {
148 - case PID_LOG_IO:
149 - #if defined(__FreeBSD__) || defined(__APPLE__)
150 - netdata_log_error("Cannot fetch process %d I/O info (command '%s')", p->pid, p->comm);
151 - #else
152 - netdata_log_error("Cannot process %s/proc/%d/io (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
153 - #endif
154 - break;
155 -
156 - case PID_LOG_STATUS:
157 - #if defined(__FreeBSD__) || defined(__APPLE__)
158 - netdata_log_error("Cannot fetch process %d status info (command '%s')", p->pid, p->comm);
159 - #else
160 - netdata_log_error("Cannot process %s/proc/%d/status (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
161 - #endif
162 - break;
163 -
164 - case PID_LOG_CMDLINE:
165 - #if defined(__FreeBSD__) || defined(__APPLE__)
166 - netdata_log_error("Cannot fetch process %d command line (command '%s')", p->pid, p->comm);
167 - #else
168 - netdata_log_error("Cannot process %s/proc/%d/cmdline (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
169 - #endif
170 - break;
171 -
172 - case PID_LOG_FDS:
173 - #if defined(__FreeBSD__) || defined(__APPLE__)
174 - netdata_log_error("Cannot fetch process %d files (command '%s')", p->pid, p->comm);
175 - #else
176 - netdata_log_error("Cannot process entries in %s/proc/%d/fd (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
177 - #endif
178 - break;
179 -
180 - case PID_LOG_LIMITS:
181 - #if defined(__FreeBSD__) || defined(__APPLE__)
182 - ;
183 - #else
184 - netdata_log_error("Cannot process %s/proc/%d/limits (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
185 - #endif
186 -
187 - case PID_LOG_STAT:
188 - break;
189 -
190 - default:
191 - netdata_log_error("unhandled error for pid %d, command '%s'", p->pid, p->comm);
192 - break;
193 - }
194 - }
195 - }
196 - errno_clear();
197 - }
198 - else if(unlikely(p->log_thrown & log)) {
199 - // netdata_log_error("unsetting log %u on pid %d", log, p->pid);
200 - p->log_thrown &= ~log;
201 - }
202 -
203 - return status;
204 -}
205 -
206 -// ----------------------------------------------------------------------------
207 -// update statistics on the targets
208 -
209 -// 1. link all childs to their parents
210 -// 2. go from bottom to top, marking as merged all children to their parents,
211 -// this step links all parents without a target to the child target, if any
212 -// 3. link all top level processes (the ones not merged) to default target
213 -// 4. go from top to bottom, linking all children without a target to their parent target
214 -// after this step all processes have a target.
215 -// [5. for each killed pid (updated = 0), remove its usage from its target]
216 -// 6. zero all apps_groups_targets
217 -// 7. concentrate all values on the apps_groups_targets
218 -// 8. remove all killed processes
219 -// 9. find the unique file count for each target
220 -// check: update_apps_groups_statistics()
221 -
222 -static void apply_apps_groups_targets_inheritance(void) {
223 - struct pid_stat *p = NULL;
224 -
225 - // children that do not have a target
226 - // inherit their target from their parent
227 - int found = 1, loops = 0;
228 - while(found) {
229 - if(unlikely(debug_enabled)) loops++;
230 - found = 0;
231 - for(p = root_of_pids; p ; p = p->next) {
232 - // if this process does not have a target,
233 - // and it has a parent
234 - // and its parent has a target
235 - // then, set the parent's target to this process
236 - if(unlikely(!p->target && p->parent && p->parent->target)) {
237 - p->target = p->parent->target;
238 - found++;
239 -
240 - if(debug_enabled || (p->target && p->target->debug_enabled))
241 - debug_log_int("TARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s).", p->target->name, p->pid, p->comm, p->parent->pid, p->parent->comm);
242 - }
243 - }
244 - }
245 -
246 - // find all the procs with 0 childs and merge them to their parents
247 - // repeat, until nothing more can be done.
248 - int sortlist = 1;
249 - found = 1;
250 - while(found) {
251 - if(unlikely(debug_enabled)) loops++;
252 - found = 0;
253 -
254 - for(p = root_of_pids; p ; p = p->next) {
255 - if(unlikely(!p->sortlist && !p->children_count))
256 - p->sortlist = sortlist++;
257 -
258 - if(unlikely(
259 - !p->children_count // if this process does not have any children
260 - && !p->merged // and is not already merged
261 - && p->parent // and has a parent
262 - && p->parent->children_count // and its parent has children
263 - // and the target of this process and its parent is the same,
264 - // or the parent does not have a target
265 - && (p->target == p->parent->target || !p->parent->target)
266 - && p->ppid != INIT_PID // and its parent is not init
267 - )) {
268 - // mark it as merged
269 - p->parent->children_count--;
270 - p->merged = true;
271 -
272 - // the parent inherits the child's target, if it does not have a target itself
273 - if(unlikely(p->target && !p->parent->target)) {
274 - p->parent->target = p->target;
275 -
276 - if(debug_enabled || (p->target && p->target->debug_enabled))
277 - debug_log_int("TARGET INHERITANCE: %s is inherited by %d (%s) from its child %d (%s).", p->target->name, p->parent->pid, p->parent->comm, p->pid, p->comm);
278 - }
279 -
280 - found++;
281 - }
282 - }
283 -
284 - debug_log("TARGET INHERITANCE: merged %d processes", found);
285 - }
286 -
287 - // init goes always to default target
288 - struct pid_stat *pi = find_pid_entry(INIT_PID);
289 - if(pi && !pi->matched_by_config)
290 - pi->target = apps_groups_default_target;
291 -
292 - // pid 0 goes always to default target
293 - pi = find_pid_entry(0);
294 - if(pi && !pi->matched_by_config)
295 - pi->target = apps_groups_default_target;
296 -
297 - // give a default target on all top level processes
298 - if(unlikely(debug_enabled)) loops++;
299 - for(p = root_of_pids; p ; p = p->next) {
300 - // if the process is not merged itself
301 - // then it is a top level process
302 - if(unlikely(!p->merged && !p->target))
303 - p->target = apps_groups_default_target;
304 -
305 - // make sure all processes have a sortlist
306 - if(unlikely(!p->sortlist))
307 - p->sortlist = sortlist++;
308 - }
309 -
310 - pi = find_pid_entry(1);
311 - if(pi)
312 - pi->sortlist = sortlist++;
313 -
314 - // give a target to all merged child processes
315 - found = 1;
316 - while(found) {
317 - if(unlikely(debug_enabled)) loops++;
318 - found = 0;
319 - for(p = root_of_pids; p ; p = p->next) {
320 - if(unlikely(!p->target && p->merged && p->parent && p->parent->target)) {
321 - p->target = p->parent->target;
322 - found++;
323 -
324 - if(debug_enabled || (p->target && p->target->debug_enabled))
325 - debug_log_int("TARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s) at phase 2.", p->target->name, p->pid, p->comm, p->parent->pid, p->parent->comm);
326 - }
327 - }
328 - }
329 -
330 - debug_log("apply_apps_groups_targets_inheritance() made %d loops on the process tree", loops);
331 -}
332 -
333 -static size_t zero_all_targets(struct target *root) {
334 - struct target *w;
335 - size_t count = 0;
336 -
337 - for (w = root; w ; w = w->next) {
338 - count++;
339 -
340 - w->minflt = 0;
341 - w->majflt = 0;
342 - w->utime = 0;
343 - w->stime = 0;
344 - w->gtime = 0;
345 - w->cminflt = 0;
346 - w->cmajflt = 0;
347 - w->cutime = 0;
348 - w->cstime = 0;
349 - w->cgtime = 0;
350 - w->num_threads = 0;
351 - // w->rss = 0;
352 - w->processes = 0;
353 -
354 - w->status_vmsize = 0;
355 - w->status_vmrss = 0;
356 - w->status_vmshared = 0;
357 - w->status_rssfile = 0;
358 - w->status_rssshmem = 0;
359 - w->status_vmswap = 0;
360 - w->status_voluntary_ctxt_switches = 0;
361 - w->status_nonvoluntary_ctxt_switches = 0;
362 -
363 - w->io_logical_bytes_read = 0;
364 - w->io_logical_bytes_written = 0;
365 - w->io_read_calls = 0;
366 - w->io_write_calls = 0;
367 - w->io_storage_bytes_read = 0;
368 - w->io_storage_bytes_written = 0;
369 - w->io_cancelled_write_bytes = 0;
370 -
371 - // zero file counters
372 - if(w->target_fds) {
373 - memset(w->target_fds, 0, sizeof(int) * w->target_fds_size);
374 - w->openfds.files = 0;
375 - w->openfds.pipes = 0;
376 - w->openfds.sockets = 0;
377 - w->openfds.inotifies = 0;
378 - w->openfds.eventfds = 0;
379 - w->openfds.timerfds = 0;
380 - w->openfds.signalfds = 0;
381 - w->openfds.eventpolls = 0;
382 - w->openfds.other = 0;
383 -
384 - w->max_open_files_percent = 0.0;
385 - }
386 -
387 - w->uptime_min = 0;
388 - w->uptime_sum = 0;
389 - w->uptime_max = 0;
390 -
391 - if(unlikely(w->root_pid)) {
392 - struct pid_on_target *pid_on_target = w->root_pid;
393 -
394 - while(pid_on_target) {
395 - struct pid_on_target *pid_on_target_to_free = pid_on_target;
396 - pid_on_target = pid_on_target->next;
397 - freez(pid_on_target_to_free);
398 - }
399 -
400 - w->root_pid = NULL;
401 - }
402 - }
403 -
404 - return count;
405 -}
406 -
407 -static inline void aggregate_pid_on_target(struct target *w, struct pid_stat *p, struct target *o) {
408 - (void)o;
409 -
410 - if(unlikely(!p->updated)) {
411 - // the process is not running
412 - return;
413 - }
414 -
415 - if(unlikely(!w)) {
416 - netdata_log_error("pid %d %s was left without a target!", p->pid, p->comm);
417 - return;
418 - }
419 -
420 - if(p->openfds_limits_percent > w->max_open_files_percent)
421 - w->max_open_files_percent = p->openfds_limits_percent;
422 -
423 - w->cutime += p->cutime;
424 - w->cstime += p->cstime;
425 - w->cgtime += p->cgtime;
426 - w->cminflt += p->cminflt;
427 - w->cmajflt += p->cmajflt;
428 -
429 - w->utime += p->utime;
430 - w->stime += p->stime;
431 - w->gtime += p->gtime;
432 - w->minflt += p->minflt;
433 - w->majflt += p->majflt;
434 -
435 - // w->rss += p->rss;
436 -
437 - w->status_vmsize += p->status_vmsize;
438 - w->status_vmrss += p->status_vmrss;
439 - w->status_vmshared += p->status_vmshared;
440 - w->status_rssfile += p->status_rssfile;
441 - w->status_rssshmem += p->status_rssshmem;
442 - w->status_vmswap += p->status_vmswap;
443 - w->status_voluntary_ctxt_switches += p->status_voluntary_ctxt_switches;
444 - w->status_nonvoluntary_ctxt_switches += p->status_nonvoluntary_ctxt_switches;
445 -
446 - w->io_logical_bytes_read += p->io_logical_bytes_read;
447 - w->io_logical_bytes_written += p->io_logical_bytes_written;
448 - w->io_read_calls += p->io_read_calls;
449 - w->io_write_calls += p->io_write_calls;
450 - w->io_storage_bytes_read += p->io_storage_bytes_read;
451 - w->io_storage_bytes_written += p->io_storage_bytes_written;
452 - w->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
453 -
454 - w->processes++;
455 - w->num_threads += p->num_threads;
456 -
457 - if(!w->uptime_min || p->uptime < w->uptime_min) w->uptime_min = p->uptime;
458 - if(!w->uptime_max || w->uptime_max < p->uptime) w->uptime_max = p->uptime;
459 - w->uptime_sum += p->uptime;
460 -
461 - if(unlikely(debug_enabled || w->debug_enabled)) {
462 - debug_log_int("aggregating '%s' pid %d on target '%s' utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", gtime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", cgtime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT "", p->comm, p->pid, w->name, p->utime, p->stime, p->gtime, p->cutime, p->cstime, p->cgtime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
463 -
464 - struct pid_on_target *pid_on_target = mallocz(sizeof(struct pid_on_target));
465 - pid_on_target->pid = p->pid;
466 - pid_on_target->next = w->root_pid;
467 - w->root_pid = pid_on_target;
468 - }
469 -}
470 -
471 -static void calculate_netdata_statistics(void) {
472 - apply_apps_groups_targets_inheritance();
473 -
474 - zero_all_targets(users_root_target);
475 - zero_all_targets(groups_root_target);
476 - apps_groups_targets_count = zero_all_targets(apps_groups_root_target);
477 -
478 - // this has to be done, before the cleanup
479 - struct pid_stat *p = NULL;
480 - struct target *w = NULL, *o = NULL;
481 -
482 - // concentrate everything on the targets
483 - for(p = root_of_pids; p ; p = p->next) {
484 -
485 - // --------------------------------------------------------------------
486 - // apps_groups target
487 -
488 - aggregate_pid_on_target(p->target, p, NULL);
489 -
490 -
491 - // --------------------------------------------------------------------
492 - // user target
493 -
494 - o = p->user_target;
495 - if(likely(p->user_target && p->user_target->uid == p->uid))
496 - w = p->user_target;
497 - else {
498 - if(unlikely(debug_enabled && p->user_target))
499 - debug_log("pid %d (%s) switched user from %u (%s) to %u.", p->pid, p->comm, p->user_target->uid, p->user_target->name, p->uid);
500 -
501 - w = p->user_target = get_users_target(p->uid);
502 - }
503 -
504 - aggregate_pid_on_target(w, p, o);
505 -
506 -
507 - // --------------------------------------------------------------------
508 - // user group target
509 -
510 - o = p->group_target;
511 - if(likely(p->group_target && p->group_target->gid == p->gid))
512 - w = p->group_target;
513 - else {
514 - if(unlikely(debug_enabled && p->group_target))
515 - debug_log("pid %d (%s) switched group from %u (%s) to %u.", p->pid, p->comm, p->group_target->gid, p->group_target->name, p->gid);
516 -
517 - w = p->group_target = get_groups_target(p->gid);
518 - }
519 -
520 - aggregate_pid_on_target(w, p, o);
521 -
522 -
523 - // --------------------------------------------------------------------
524 - // aggregate all file descriptors
525 -
526 - if(enable_file_charts)
527 - aggregate_pid_fds_on_targets(p);
528 - }
529 -
530 - cleanup_exited_pids();
531 -}
532 -
121 // ----------------------------------------------------------------------------
122 // update chart dimensions
123
124 +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
125 static void normalize_utilization(struct target *root) {
126 struct target *w;
127
@@ -540,7 +129,7 @@ static void normalize_utilization(struct target *root) {
129 // here we try to eliminate them by disabling childs processing either for specific dimensions
130 // or entirely. Of course, either way, we disable it just a single iteration.
131
543 - kernel_uint_t max_time = os_get_system_cpus() * time_factor * RATES_DETAIL;
132 + kernel_uint_t max_time = os_get_system_cpus() * NSEC_PER_SEC;
133 kernel_uint_t utime = 0, cutime = 0, stime = 0, cstime = 0, gtime = 0, cgtime = 0, minflt = 0, cminflt = 0, majflt = 0, cmajflt = 0;
134
135 if(global_utime > max_time) global_utime = max_time;
@@ -548,19 +137,19 @@ static void normalize_utilization(struct target *root) {
137 if(global_gtime > max_time) global_gtime = max_time;
138
139 for(w = root; w ; w = w->next) {
551 - if(w->target || (!w->processes && !w->exposed)) continue;
552 -
553 - utime += w->utime;
554 - stime += w->stime;
555 - gtime += w->gtime;
556 - cutime += w->cutime;
557 - cstime += w->cstime;
558 - cgtime += w->cgtime;
559 -
560 - minflt += w->minflt;
561 - majflt += w->majflt;
562 - cminflt += w->cminflt;
563 - cmajflt += w->cmajflt;
140 + if(w->target || (!w->values[PDF_PROCESSES] && !w->exposed)) continue;
141 +
142 + utime += w->values[PDF_UTIME];
143 + stime += w->values[PDF_STIME];
144 + gtime += w->values[PDF_GTIME];
145 + cutime += w->values[PDF_CUTIME];
146 + cstime += w->values[PDF_CSTIME];
147 + cgtime += w->values[PDF_CGTIME];
148 +
149 + minflt += w->values[PDF_MINFLT];
150 + majflt += w->values[PDF_MAJFLT];
151 + cminflt += w->values[PDF_CMINFLT];
152 + cmajflt += w->values[PDF_CMAJFLT];
153 }
154
155 if(global_utime || global_stime || global_gtime) {
@@ -683,6 +272,7 @@ static void normalize_utilization(struct target *root) {
272 , (kernel_uint_t)(cgtime * cgtime_fix_ratio)
273 );
274 }
275 +#endif
276
277 // ----------------------------------------------------------------------------
278 // parse command line arguments
@@ -690,6 +280,7 @@ static void normalize_utilization(struct target *root) {
280 int check_proc_1_io() {
281 int ret = 0;
282
283 +#if defined(OS_LINUX)
284 procfile *ff = procfile_open("/proc/1/io", NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
285 if(!ff) goto cleanup;
286
@@ -700,9 +291,13 @@ int check_proc_1_io() {
291
292 cleanup:
293 procfile_close(ff);
294 +#endif
295 +
296 return ret;
297 }
298
299 +static bool profile_speed = false;
300 +
301 static void parse_args(int argc, char **argv)
302 {
303 int i, freq = 0;
@@ -721,6 +316,7 @@ static void parse_args(int argc, char **argv)
316 exit(0);
317 }
318
319 +#if defined(OS_LINUX)
320 if(strcmp("test-permissions", argv[i]) == 0 || strcmp("-t", argv[i]) == 0) {
321 if(!check_proc_1_io()) {
322 perror("Tried to read /proc/1/io and it failed");
@@ -729,6 +325,7 @@ static void parse_args(int argc, char **argv)
325 printf("OK\n");
326 exit(0);
327 }
328 +#endif
329
330 if(strcmp("debug", argv[i]) == 0) {
331 debug_enabled = true;
@@ -738,7 +335,12 @@ static void parse_args(int argc, char **argv)
335 continue;
336 }
337
741 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
338 + if(strcmp("profile-speed", argv[i]) == 0) {
339 + profile_speed = true;
340 + continue;
341 + }
342 +
343 +#if defined(OS_LINUX)
344 if(strcmp("fds-cache-secs", argv[i]) == 0) {
345 if(argc <= i + 1) {
346 fprintf(stderr, "Parameter 'fds-cache-secs' requires a number as argument.\n");
@@ -751,6 +353,7 @@ static void parse_args(int argc, char **argv)
353 }
354 #endif
355
356 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1) || (PROCESSES_HAVE_CHILDREN_FLTS == 1)
357 if(strcmp("no-childs", argv[i]) == 0 || strcmp("without-childs", argv[i]) == 0) {
358 include_exited_childs = 0;
359 continue;
@@ -760,7 +363,9 @@ static void parse_args(int argc, char **argv)
363 include_exited_childs = 1;
364 continue;
365 }
366 +#endif
367
368 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
369 if(strcmp("with-guest", argv[i]) == 0) {
370 enable_guest_charts = true;
371 continue;
@@ -770,7 +375,9 @@ static void parse_args(int argc, char **argv)
375 enable_guest_charts = false;
376 continue;
377 }
378 +#endif
379
380 +#if (PROCESSES_HAVE_FDS == 1)
381 if(strcmp("with-files", argv[i]) == 0) {
382 enable_file_charts = 1;
383 continue;
@@ -780,16 +387,21 @@ static void parse_args(int argc, char **argv)
387 enable_file_charts = 0;
388 continue;
389 }
390 +#endif
391
392 +#if (PROCESSES_HAVE_UID == 1)
393 if(strcmp("no-users", argv[i]) == 0 || strcmp("without-users", argv[i]) == 0) {
394 enable_users_charts = 0;
395 continue;
396 }
397 +#endif
398
399 +#if (PROCESSES_HAVE_GID == 1)
400 if(strcmp("no-groups", argv[i]) == 0 || strcmp("without-groups", argv[i]) == 0) {
401 enable_groups_charts = 0;
402 continue;
403 }
404 +#endif
405
406 if(strcmp("with-detailed-uptime", argv[i]) == 0) {
407 enable_detailed_uptime_charts = 1;
@@ -821,26 +433,36 @@ static void parse_args(int argc, char **argv)
433 " it may include sensitive data such as passwords and tokens\n"
434 " enabling this could be a security risk\n"
435 "\n"
436 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1) || (PROCESSES_HAVE_CHILDREN_FLTS == 1)
437 " with-childs\n"
438 " without-childs enable / disable aggregating exited\n"
439 " children resources into parents\n"
440 " (default is enabled)\n"
441 "\n"
442 +#endif
443 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
444 " with-guest\n"
445 " without-guest enable / disable reporting guest charts\n"
446 " (default is disabled)\n"
447 "\n"
448 +#endif
449 +#if (PROCESSES_HAVE_FDS == 1)
450 " with-files\n"
451 " without-files enable / disable reporting files, sockets, pipes\n"
452 " (default is enabled)\n"
453 "\n"
454 +#endif
455 +#if (PROCESSES_HAVE_UID == 1)
456 " without-users disable reporting per user charts\n"
457 "\n"
458 +#endif
459 +#if (PROCESSES_HAVE_GID == 1)
460 " without-groups disable reporting per user group charts\n"
461 "\n"
462 +#endif
463 " with-detailed-uptime enable reporting min/avg/max uptime charts\n"
464 "\n"
843 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
465 +#if defined(OS_LINUX)
466 " fds-cache-secs N cache the files of processed for N seconds\n"
467 " caching is adaptive per file (when a file\n"
468 " is found, it starts at 0 and while the file\n"
@@ -852,15 +474,17 @@ static void parse_args(int argc, char **argv)
474 " version or -v or -V print program version and exit\n"
475 "\n"
476 , NETDATA_VERSION
855 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
477 +#if defined(OS_LINUX)
478 , max_fds_cache_seconds
479 #endif
480 );
859 - exit(1);
481 + exit(0);
482 }
483
484 +#if !defined(OS_WINDOWS) || !defined(RUN_UNDER_CLION)
485 netdata_log_error("Cannot understand option %s", argv[i]);
486 exit(1);
487 +#endif
488 }
489
490 if(freq > 0) update_every = freq;
@@ -879,7 +503,8 @@ static void parse_args(int argc, char **argv)
503 netdata_log_info("Loaded config file '%s/apps_groups.conf'", user_config_dir);
504 }
505
882 -static int am_i_running_as_root() {
506 +#if !defined(OS_WINDOWS)
507 +static inline int am_i_running_as_root() {
508 uid_t uid = getuid(), euid = geteuid();
509
510 if(uid == 0 || euid == 0) {
@@ -892,7 +517,7 @@ static int am_i_running_as_root() {
517 }
518
519 #ifdef HAVE_SYS_CAPABILITY_H
895 -static int check_capabilities() {
520 +static inline int check_capabilities() {
521 cap_t caps = cap_get_proc();
522 if(!caps) {
523 netdata_log_error("Cannot get current capabilities.");
@@ -936,22 +561,13 @@ static int check_capabilities() {
561 return ret;
562 }
563 #else
939 -static int check_capabilities() {
564 +static inline int check_capabilities() {
565 return 0;
566 }
567 #endif
568 +#endif
569
944 -static netdata_mutex_t apps_and_stdout_mutex = NETDATA_MUTEX_INITIALIZER;
945 -
946 -struct target *find_target_by_name(struct target *base, const char *name) {
947 - struct target *t;
948 - for(t = base; t ; t = t->next) {
949 - if (strcmp(t->name, name) == 0)
950 - return t;
951 - }
952 -
953 - return NULL;
954 -}
570 +netdata_mutex_t apps_and_stdout_mutex = NETDATA_MUTEX_INITIALIZER;
571
572 static bool apps_plugin_exit = false;
573
@@ -1000,47 +616,35 @@ int main(int argc, char **argv) {
616 #endif /* NETDATA_INTERNAL_CHECKS */
617
618 procfile_adaptive_initial_allocation = 1;
1003 -
619 os_get_system_HZ();
1005 -#if defined(__FreeBSD__)
1006 - time_factor = 1000000ULL / RATES_DETAIL; // FreeBSD uses usecs
1007 -#endif
1008 -#if defined(__APPLE__)
1009 - mach_timebase_info(&mach_info);
1010 - time_factor = 1000000ULL / RATES_DETAIL;
1011 -#endif
1012 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
1013 - time_factor = system_hz; // Linux uses clock ticks
1014 -#endif
1015 -
1016 - os_get_system_pid_max();
620 os_get_system_cpus_uncached();
1018 -
621 + apps_orchestrators_and_aggregators_init(); // before parsing args!
622 parse_args(argc, argv);
623
624 +#if !defined(OS_WINDOWS)
625 if(!check_capabilities() && !am_i_running_as_root() && !check_proc_1_io()) {
626 uid_t uid = getuid(), euid = geteuid();
627 #ifdef HAVE_SYS_CAPABILITY_H
628 netdata_log_error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
1025 - "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
1026 - "To enable capabilities run: sudo setcap cap_dac_read_search,cap_sys_ptrace+ep %s; "
1027 - "To enable setuid to root run: sudo chown root:netdata %s; sudo chmod 4750 %s; "
1028 - , uid, euid, argv[0], argv[0], argv[0]
1029 - );
629 + "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
630 + "To enable capabilities run: sudo setcap cap_dac_read_search,cap_sys_ptrace+ep %s; "
631 + "To enable setuid to root run: sudo chown root:netdata %s; sudo chmod 4750 %s; "
632 + , uid, euid, argv[0], argv[0], argv[0]);
633 #else
634 netdata_log_error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
1032 - "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
1033 - "Your system does not support capabilities. "
1034 - "To enable setuid to root run: sudo chown root:netdata %s; sudo chmod 4750 %s; "
1035 - , uid, euid, argv[0], argv[0]
1036 - );
635 + "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
636 + "Your system does not support capabilities. "
637 + "To enable setuid to root run: sudo chown root:netdata %s; sudo chmod 4750 %s; "
638 + , uid, euid, argv[0], argv[0]);
639 #endif
640 }
641 +#endif
642
643 netdata_log_info("started on pid %d", getpid());
644
1042 - users_and_groups_init();
1043 - pids_init();
645 + apps_users_and_groups_init();
646 + apps_pids_init();
647 + OS_FUNCTION(apps_os_init)();
648
649 // ------------------------------------------------------------------------
650 // the event loop for functions
@@ -1062,15 +666,16 @@ int main(int argc, char **argv) {
666 for(; !apps_plugin_exit ; global_iterations_counter++) {
667 netdata_mutex_unlock(&apps_and_stdout_mutex);
668
1065 -#ifdef NETDATA_PROFILING
1066 -#warning "compiling for profiling"
1067 - static int profiling_count=0;
1068 - profiling_count++;
1069 - if(unlikely(profiling_count > 2000)) exit(0);
1070 - usec_t dt = update_every * USEC_PER_SEC;
1071 -#else
1072 - usec_t dt = heartbeat_next(&hb, step);
1073 -#endif
669 + usec_t dt;
670 + if(profile_speed) {
671 + static int profiling_count=0;
672 + profiling_count++;
673 + if(unlikely(profiling_count > 500)) exit(0);
674 + dt = update_every * USEC_PER_SEC;
675 + }
676 + else
677 + dt = heartbeat_next(&hb, step);
678 +
679 netdata_mutex_lock(&apps_and_stdout_mutex);
680
681 struct pollfd pollfd = { .fd = fileno(stdout), .events = POLLERR };
@@ -1083,9 +688,6 @@ int main(int argc, char **argv) {
688 fatal("Received error on read pipe.");
689 }
690
1086 - if(global_iterations_counter % 10 == 0)
1087 - get_MemTotal();
1088 -
691 if(!collect_data_for_all_pids()) {
692 netdata_log_error("Cannot collect /proc data for running processes. Disabling apps.plugin...");
693 printf("DISABLE\n");
@@ -1093,30 +695,39 @@ int main(int argc, char **argv) {
695 exit(1);
696 }
697
1096 - calculate_netdata_statistics();
698 + aggregate_processes_to_targets();
699 +
700 +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
701 + OS_FUNCTION(apps_os_read_global_cpu_utilization)();
702 normalize_utilization(apps_groups_root_target);
703 +#endif
704
705 if(send_resource_usage)
706 send_resource_usage_to_netdata(dt);
707
708 +#if (PROCESSES_HAVE_STATE == 1)
709 send_proc_states_count(dt);
1103 - send_charts_updates_to_netdata(apps_groups_root_target, "app", "app_group", "Apps");
710 +#endif
711 +
712 + send_charts_updates_to_netdata(apps_groups_root_target, "app", "app_group", "Applications Groups");
713 send_collected_data_to_netdata(apps_groups_root_target, "app", dt);
714
715 +#if (PROCESSES_HAVE_UID == 1)
716 if (enable_users_charts) {
1107 - send_charts_updates_to_netdata(users_root_target, "user", "user", "Users");
717 + send_charts_updates_to_netdata(users_root_target, "user", "user", "User Processes");
718 send_collected_data_to_netdata(users_root_target, "user", dt);
719 }
720 +#endif
721
722 +#if (PROCESSES_HAVE_GID == 1)
723 if (enable_groups_charts) {
1112 - send_charts_updates_to_netdata(groups_root_target, "usergroup", "user_group", "User Groups");
724 + send_charts_updates_to_netdata(groups_root_target, "usergroup", "user_group", "User Group Processes");
725 send_collected_data_to_netdata(groups_root_target, "usergroup", dt);
726 }
727 +#endif
728
729 fflush(stdout);
730
1118 - show_guest_time_old = show_guest_time;
1119 -
731 debug_log("done Loop No %zu", global_iterations_counter);
732 }
733 netdata_mutex_unlock(&apps_and_stdout_mutex);
src/collectors/apps.plugin/apps_plugin.h
+444 -253
@@ -6,11 +6,40 @@
6 #include "collectors/all.h"
7 #include "libnetdata/libnetdata.h"
8
9 -#ifdef __FreeBSD__
9 +#define OS_FUNC_CONCAT(a, b) a##b
10 +
11 +#if defined(OS_FREEBSD)
12 #include <sys/user.h>
11 -#endif
13
13 -#ifdef __APPLE__
14 +#define OS_INIT_PID 1
15 +#define ALL_PIDS_ARE_READ_INSTANTLY 1
16 +#define PROCESSES_HAVE_CPU_GUEST_TIME 0
17 +#define PROCESSES_HAVE_CPU_CHILDREN_TIME 1
18 +#define PROCESSES_HAVE_VOLCTX 0
19 +#define PROCESSES_HAVE_NVOLCTX 0
20 +#define PROCESSES_HAVE_PHYSICAL_IO 0
21 +#define PROCESSES_HAVE_LOGICAL_IO 1
22 +#define PROCESSES_HAVE_IO_CALLS 0
23 +#define PROCESSES_HAVE_UID 1
24 +#define PROCESSES_HAVE_GID 1
25 +#define PROCESSES_HAVE_MAJFLT 1
26 +#define PROCESSES_HAVE_CHILDREN_FLTS 1
27 +#define PROCESSES_HAVE_VMSWAP 0
28 +#define PROCESSES_HAVE_VMSHARED 0
29 +#define PROCESSES_HAVE_RSSFILE 0
30 +#define PROCESSES_HAVE_RSSSHMEM 0
31 +#define PROCESSES_HAVE_FDS 1
32 +#define PROCESSES_HAVE_HANDLES 0
33 +#define PROCESSES_HAVE_CMDLINE 1
34 +#define PROCESSES_HAVE_PID_LIMITS 0
35 +#define PROCESSES_HAVE_COMM_AND_NAME 0
36 +#define PROCESSES_HAVE_STATE 0
37 +#define PPID_SHOULD_BE_RUNNING 1
38 +#define INCREMENTAL_DATA_COLLECTION 1
39 +#define CPU_TO_NANOSECONDCORES (1000) // convert microseconds to nanoseconds
40 +#define OS_FUNCTION(func) OS_FUNC_CONCAT(func, _freebsd)
41 +
42 +#elif defined(OS_MACOS)
43 #include <mach/mach.h>
44 #include <mach/mach_host.h>
45 #include <libproc.h>
@@ -18,40 +47,114 @@
47 #include <sys/sysctl.h>
48 #include <mach/mach_time.h> // For mach_timebase_info_data_t and mach_timebase_info
49
21 -extern mach_timebase_info_data_t mach_info;
22 -#endif
23 -
24 -// ----------------------------------------------------------------------------
25 -// per O/S configuration
26 -
27 -// the minimum PID of the system
28 -// this is also the pid of the init process
29 -#define INIT_PID 1
30 -
31 -// if the way apps.plugin will work, will read the entire process list,
32 -// including the resource utilization of each process, instantly
33 -// set this to 1
34 -// when set to 0, apps.plugin builds a sort list of processes, in order
35 -// to process children processes, before parent processes
36 -#if defined(__FreeBSD__) || defined(__APPLE__)
37 -#define ALL_PIDS_ARE_READ_INSTANTLY 1
38 -#else
39 -#define ALL_PIDS_ARE_READ_INSTANTLY 0
40 -#endif
41 -
42 -#if defined(__APPLE__)
50 struct pid_info {
51 struct kinfo_proc proc;
52 struct proc_taskinfo taskinfo;
53 struct proc_bsdinfo bsdinfo;
54 struct rusage_info_v4 rusageinfo;
55 };
56 +
57 +#define OS_INIT_PID 1
58 +#define ALL_PIDS_ARE_READ_INSTANTLY 1
59 +#define PROCESSES_HAVE_CPU_GUEST_TIME 0
60 +#define PROCESSES_HAVE_CPU_CHILDREN_TIME 0
61 +#define PROCESSES_HAVE_VOLCTX 1
62 +#define PROCESSES_HAVE_NVOLCTX 0
63 +#define PROCESSES_HAVE_PHYSICAL_IO 0
64 +#define PROCESSES_HAVE_LOGICAL_IO 1
65 +#define PROCESSES_HAVE_IO_CALLS 0
66 +#define PROCESSES_HAVE_UID 1
67 +#define PROCESSES_HAVE_GID 1
68 +#define PROCESSES_HAVE_MAJFLT 1
69 +#define PROCESSES_HAVE_CHILDREN_FLTS 0
70 +#define PROCESSES_HAVE_VMSWAP 0
71 +#define PROCESSES_HAVE_VMSHARED 0
72 +#define PROCESSES_HAVE_RSSFILE 0
73 +#define PROCESSES_HAVE_RSSSHMEM 0
74 +#define PROCESSES_HAVE_FDS 1
75 +#define PROCESSES_HAVE_HANDLES 0
76 +#define PROCESSES_HAVE_CMDLINE 1
77 +#define PROCESSES_HAVE_PID_LIMITS 0
78 +#define PROCESSES_HAVE_COMM_AND_NAME 0
79 +#define PROCESSES_HAVE_STATE 0
80 +#define PPID_SHOULD_BE_RUNNING 1
81 +#define INCREMENTAL_DATA_COLLECTION 1
82 +#define CPU_TO_NANOSECONDCORES (1) // already in nanoseconds
83 +#define OS_FUNCTION(func) OS_FUNC_CONCAT(func, _macos)
84 +
85 +#elif defined(OS_WINDOWS)
86 +#include <windows.h>
87 +
88 +#define OS_INIT_PID 0 // dynamic, is set during data collection
89 +#define ALL_PIDS_ARE_READ_INSTANTLY 1
90 +#define PROCESSES_HAVE_CPU_GUEST_TIME 0
91 +#define PROCESSES_HAVE_CPU_CHILDREN_TIME 0
92 +#define PROCESSES_HAVE_VOLCTX 0
93 +#define PROCESSES_HAVE_NVOLCTX 0
94 +#define PROCESSES_HAVE_PHYSICAL_IO 0
95 +#define PROCESSES_HAVE_LOGICAL_IO 1
96 +#define PROCESSES_HAVE_IO_CALLS 1
97 +#define PROCESSES_HAVE_UID 0
98 +#define PROCESSES_HAVE_GID 0
99 +#define PROCESSES_HAVE_MAJFLT 0
100 +#define PROCESSES_HAVE_CHILDREN_FLTS 0
101 +#define PROCESSES_HAVE_VMSWAP 1
102 +#define PROCESSES_HAVE_VMSHARED 0
103 +#define PROCESSES_HAVE_RSSFILE 0
104 +#define PROCESSES_HAVE_RSSSHMEM 0
105 +#define PROCESSES_HAVE_FDS 0
106 +#define PROCESSES_HAVE_HANDLES 1
107 +#define PROCESSES_HAVE_CMDLINE 0
108 +#define PROCESSES_HAVE_PID_LIMITS 0
109 +#define PROCESSES_HAVE_COMM_AND_NAME 1
110 +#define PROCESSES_HAVE_STATE 0
111 +#define PPID_SHOULD_BE_RUNNING 0
112 +#define INCREMENTAL_DATA_COLLECTION 0
113 +#define CPU_TO_NANOSECONDCORES (100) // convert 100ns to ns
114 +#define OS_FUNCTION(func) OS_FUNC_CONCAT(func, _windows)
115 +
116 +#elif defined(OS_LINUX)
117 +#define OS_INIT_PID 1
118 +#define ALL_PIDS_ARE_READ_INSTANTLY 0
119 +#define PROCESSES_HAVE_CPU_GUEST_TIME 1
120 +#define PROCESSES_HAVE_CPU_CHILDREN_TIME 1
121 +#define PROCESSES_HAVE_VOLCTX 1
122 +#define PROCESSES_HAVE_NVOLCTX 1
123 +#define PROCESSES_HAVE_PHYSICAL_IO 1
124 +#define PROCESSES_HAVE_LOGICAL_IO 1
125 +#define PROCESSES_HAVE_IO_CALLS 1
126 +#define PROCESSES_HAVE_UID 1
127 +#define PROCESSES_HAVE_GID 1
128 +#define PROCESSES_HAVE_MAJFLT 1
129 +#define PROCESSES_HAVE_CHILDREN_FLTS 1
130 +#define PROCESSES_HAVE_VMSWAP 1
131 +#define PROCESSES_HAVE_VMSHARED 1
132 +#define PROCESSES_HAVE_RSSFILE 1
133 +#define PROCESSES_HAVE_RSSSHMEM 1
134 +#define PROCESSES_HAVE_FDS 1
135 +#define PROCESSES_HAVE_HANDLES 0
136 +#define PROCESSES_HAVE_CMDLINE 1
137 +#define PROCESSES_HAVE_PID_LIMITS 1
138 +#define PROCESSES_HAVE_COMM_AND_NAME 0
139 +#define PROCESSES_HAVE_STATE 1
140 +#define PPID_SHOULD_BE_RUNNING 1
141 +#define USE_APPS_GROUPS_CONF 1
142 +#define INCREMENTAL_DATA_COLLECTION 1
143 +#define CPU_TO_NANOSECONDCORES (NSEC_PER_SEC / system_hz)
144 +#define OS_FUNCTION(func) OS_FUNC_CONCAT(func, _linux)
145 +
146 +extern int max_fds_cache_seconds;
147 +
148 +#else
149 +#error "Unsupported operating system"
150 #endif
151
51 -// ----------------------------------------------------------------------------
152 +// --------------------------------------------------------------------------------------------------------------------
153 +
154 +extern pid_t INIT_PID;
155
156 extern bool debug_enabled;
54 -extern bool enable_guest_charts;
157 +
158 extern bool enable_detailed_uptime_charts;
159 extern bool enable_users_charts;
160 extern bool enable_groups_charts;
@@ -68,19 +171,23 @@ extern size_t
171 inodes_changed_counter,
172 links_changed_counter,
173 targets_assignment_counter,
71 - all_pids_count,
174 apps_groups_targets_count;
175
74 -extern int
176 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
177 +extern bool enable_guest_charts;
178 +extern bool show_guest_time;
179 +#endif
180 +
181 +extern uint32_t
182 all_files_len,
76 - all_files_size,
77 - show_guest_time,
78 - show_guest_time_old;
183 + all_files_size;
184
185 +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
186 extern kernel_uint_t
187 global_utime,
188 global_stime,
189 global_gtime;
190 +#endif
191
192 // the normalization ratios, as calculated by normalize_utilization()
193 extern NETDATA_DOUBLE
@@ -95,19 +202,13 @@ extern NETDATA_DOUBLE
202 cminflt_fix_ratio,
203 cmajflt_fix_ratio;
204
98 -#if defined(__FreeBSD__) || defined(__APPLE__)
99 -extern usec_t system_current_time_ut;
100 -#else
101 -extern kernel_uint_t system_uptime_secs;
102 -#endif
103 -
205 extern size_t pagesize;
206
207 +extern netdata_mutex_t apps_and_stdout_mutex;
208 +
209 // ----------------------------------------------------------------------------
210 // string lengths
211
109 -#define MAX_COMPARE_NAME 100
110 -#define MAX_NAME 100
212 #define MAX_CMDLINE 65536
213
214 // ----------------------------------------------------------------------------
@@ -117,13 +218,9 @@ extern size_t pagesize;
218 // having a lot of spares, increases the CPU utilization of the plugin.
219 #define MAX_SPARE_FDS 1
220
120 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
121 -extern int max_fds_cache_seconds;
122 -#endif
123 -
221 // ----------------------------------------------------------------------------
222 // some variables for keeping track of processes count by states
126 -
223 +#if (PROCESSES_HAVE_STATE == 1)
224 typedef enum {
225 PROC_STATUS_RUNNING = 0,
226 PROC_STATUS_SLEEPING_D, // uninterruptible sleep
@@ -135,6 +232,7 @@ typedef enum {
232
233 extern proc_state proc_state_count[PROC_STATUS_END];
234 extern const char *proc_states[];
235 +#endif
236
237 // ----------------------------------------------------------------------------
238 // the rates we are going to send to netdata will have this detail a value of:
@@ -144,6 +242,7 @@ extern const char *proc_states[];
242 // etc.
243 #define RATES_DETAIL 10000ULL
244
245 +#if (PROCESSES_HAVE_FDS == 1)
246 struct openfds {
247 kernel_uint_t files;
248 kernel_uint_t pipes;
@@ -155,8 +254,8 @@ struct openfds {
254 kernel_uint_t eventpolls;
255 kernel_uint_t other;
256 };
158 -
257 #define pid_openfds_sum(p) ((p)->openfds.files + (p)->openfds.pipes + (p)->openfds.sockets + (p)->openfds.inotifies + (p)->openfds.eventfds + (p)->openfds.timerfds + (p)->openfds.signalfds + (p)->openfds.eventpolls + (p)->openfds.other)
258 +#endif
259
260 // ----------------------------------------------------------------------------
261 // target
@@ -172,69 +271,133 @@ struct pid_on_target {
271 struct pid_on_target *next;
272 };
273
175 -struct target {
176 - char compare[MAX_COMPARE_NAME + 1];
177 - uint32_t comparehash;
178 - size_t comparelen;
274 +typedef enum __attribute__((packed)) {
275 + TARGET_TYPE_APP_GROUP = 1,
276 +#if (PROCESSES_HAVE_UID == 1)
277 + TARGET_TYPE_UID,
278 +#endif
279 +#if (PROCESSES_HAVE_GID == 1)
280 + TARGET_TYPE_GID,
281 +#endif
282 + TARGET_TYPE_TREE,
283 +} TARGET_TYPE;
284
180 - char id[MAX_NAME + 1];
181 - uint32_t idhash;
285 +typedef enum __attribute__((packed)) {
286 + // CPU utilization time
287 + // The values are expressed in "NANOSECONDCORES".
288 + // 1 x "NANOSECONDCORE" = 1 x NSEC_PER_SEC (1 billion).
289 + PDF_UTIME, // CPU user time
290 + PDF_STIME, // CPU system time
291 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
292 + PDF_GTIME, // CPU guest time
293 +#endif
294 +#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
295 + PDF_CUTIME, // exited children CPU user time
296 + PDF_CSTIME, // exited children CPU system time
297 +#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
298 + PDF_CGTIME, // exited children CPU guest time
299 +#endif
300 +#endif
301
183 - char name[MAX_NAME + 1];
184 - char clean_name[MAX_NAME + 1]; // sanitized name used in chart id (need to replace at least dots)
185 - uid_t uid;
186 - gid_t gid;
302 + PDF_MINFLT, // rate, unit: faults * RATES_DETAIL
303 +
304 +#if (PROCESSES_HAVE_MAJFLT == 1)
305 + PDF_MAJFLT, // rate, unit: faults * RATES_DETAIL
306 +#endif
307
188 - bool is_other;
189 -
190 - kernel_uint_t minflt;
191 - kernel_uint_t cminflt;
192 - kernel_uint_t majflt;
193 - kernel_uint_t cmajflt;
194 - kernel_uint_t utime;
195 - kernel_uint_t stime;
196 - kernel_uint_t gtime;
197 - kernel_uint_t cutime;
198 - kernel_uint_t cstime;
199 - kernel_uint_t cgtime;
200 - kernel_uint_t num_threads;
201 - // kernel_uint_t rss;
202 -
203 - kernel_uint_t status_vmsize;
204 - kernel_uint_t status_vmrss;
205 - kernel_uint_t status_vmshared;
206 - kernel_uint_t status_rssfile;
207 - kernel_uint_t status_rssshmem;
208 - kernel_uint_t status_vmswap;
209 - kernel_uint_t status_voluntary_ctxt_switches;
210 - kernel_uint_t status_nonvoluntary_ctxt_switches;
211 -
212 - kernel_uint_t io_logical_bytes_read;
213 - kernel_uint_t io_logical_bytes_written;
214 - kernel_uint_t io_read_calls;
215 - kernel_uint_t io_write_calls;
216 - kernel_uint_t io_storage_bytes_read;
217 - kernel_uint_t io_storage_bytes_written;
218 - kernel_uint_t io_cancelled_write_bytes;
308 +#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
309 + PDF_CMINFLT, // rate, unit: faults * RATES_DETAIL
310 + PDF_CMAJFLT, // rate, unit: faults * RATES_DETAIL
311 +#endif
312
220 - int *target_fds;
221 - int target_fds_size;
313 + PDF_VMSIZE, // the current virtual memory used by the process, in bytes
314 + PDF_VMRSS, // the resident memory used by the process, in bytes
315
223 - struct openfds openfds;
316 +#if (PROCESSES_HAVE_VMSHARED == 1)
317 + PDF_VMSHARED, // the shared memory used by the process, in bytes
318 +#endif
319
225 - NETDATA_DOUBLE max_open_files_percent;
320 +#if (PROCESSES_HAVE_RSSFILE == 1)
321 + PDF_RSSFILE, // unit: bytes
322 +#endif
323 +
324 +#if (PROCESSES_HAVE_RSSSHMEM == 1)
325 + PDF_RSSSHMEM, // unit: bytes
326 +#endif
327 +
328 +#if (PROCESSES_HAVE_VMSWAP == 1)
329 + PDF_VMSWAP, // the swap memory used by the process, in bytes
330 +#endif
331 +
332 +#if (PROCESSES_HAVE_VOLCTX == 1)
333 + PDF_VOLCTX, // rate, unit: switches * RATES_DETAIL
334 +#endif
335 +
336 +#if (PROCESSES_HAVE_NVOLCTX == 1)
337 + PDF_NVOLCTX, // rate, unit: switches * RATES_DETAIL
338 +#endif
339 +
340 +#if (PROCESSES_HAVE_LOGICAL_IO == 1)
341 + PDF_LREAD, // rate, logical reads in bytes/sec * RATES_DETAIL
342 + PDF_LWRITE, // rate, logical writes in bytes/sec * RATES_DETAIL
343 +#endif
344 +
345 +#if (PROCESSES_HAVE_PHYSICAL_IO == 1)
346 + PDF_PREAD, // rate, physical reads in bytes/sec * RATES_DETAIL
347 + PDF_PWRITE, // rate, physical writes in bytes/sec * RATES_DETAIL
348 +#endif
349 +
350 +#if (PROCESSES_HAVE_IO_CALLS == 1)
351 + PDF_OREAD, // rate, read ops/sec * RATES_DETAIL
352 + PDF_OWRITE, // rate, write ops/sec * RATES_DETAIL
353 +#endif
354 +
355 + PDF_UPTIME, // the process uptime in seconds
356 + PDF_THREADS, // the number of threads
357 + PDF_PROCESSES, // the number of processes
358 +
359 +#if (PROCESSES_HAVE_HANDLES == 1)
360 + PDF_HANDLES, // the number of handles the process maintains
361 +#endif
362 +
363 + // terminator
364 + PDF_MAX
365 +} PID_FIELD;
366 +
367 +struct target {
368 + STRING *id;
369 + STRING *name;
370 + STRING *clean_name;
371 +
372 + TARGET_TYPE type;
373 + union {
374 + STRING *compare;
375 +#if (PROCESSES_HAVE_UID == 1)
376 + uid_t uid;
377 +#endif
378 +#if (PROCESSES_HAVE_GID == 1)
379 + gid_t gid;
380 +#endif
381 + };
382 +
383 + kernel_uint_t values[PDF_MAX];
384
385 kernel_uint_t uptime_min;
228 - kernel_uint_t uptime_sum;
386 kernel_uint_t uptime_max;
387
231 - unsigned int processes; // how many processes have been merged to this
232 - int exposed; // if set, we have sent this to netdata
233 - int hidden; // if set, we set the hidden flag on the dimension
234 - int debug_enabled;
235 - int ends_with;
236 - int starts_with; // if set, the compare string matches only the
237 - // beginning of the command
388 +#if (PROCESSES_HAVE_FDS == 1)
389 + struct openfds openfds;
390 + NETDATA_DOUBLE max_open_files_percent;
391 + int *target_fds;
392 + uint32_t target_fds_size;
393 +#endif
394 +
395 + bool exposed:1; // if set, we have sent this to netdata
396 + bool hidden:1; // if set, we set the hidden flag on the dimension
397 + bool debug_enabled:1;
398 + bool ends_with:1;
399 + bool starts_with:1; // if set, the compare string matches only the
400 + // beginning of the command
401
402 struct pid_on_target *root_pid; // list of aggregated pids for target debugging
403
@@ -264,6 +427,7 @@ typedef enum __attribute__((packed)) {
427 // structure to store data for each process running
428 // see: man proc for the description of the fields
429
430 +#if (PROCESSES_HAVE_PID_LIMITS == 1)
431 struct pid_limits {
432 // kernel_uint_t max_cpu_time;
433 // kernel_uint_t max_file_size;
@@ -282,11 +446,12 @@ struct pid_limits {
446 // kernel_uint_t max_realtime_priority;
447 // kernel_uint_t max_realtime_timeout;
448 };
449 +#endif
450
451 struct pid_fd {
452 int fd;
453
289 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
454 +#if defined(OS_LINUX)
455 ino_t inode;
456 char *filename;
457 uint32_t link_hash;
@@ -295,6 +460,9 @@ struct pid_fd {
460 #endif
461 };
462
463 +#define pid_stat_comm(p) (string2str(p->comm))
464 +#define pid_stat_cmdline(p) (string2str(p->cmdline))
465 +
466 struct pid_stat {
467 int32_t pid;
468 int32_t ppid;
@@ -304,122 +472,77 @@ struct pid_stat {
472 // int32_t tpgid;
473 // uint64_t flags;
474
307 - char state;
308 -
309 - char comm[MAX_COMPARE_NAME + 1];
310 - char *cmdline;
311 -
312 - // these are raw values collected
313 - kernel_uint_t minflt_raw;
314 - kernel_uint_t cminflt_raw;
315 - kernel_uint_t majflt_raw;
316 - kernel_uint_t cmajflt_raw;
317 - kernel_uint_t utime_raw;
318 - kernel_uint_t stime_raw;
319 - kernel_uint_t gtime_raw; // guest_time
320 - kernel_uint_t cutime_raw;
321 - kernel_uint_t cstime_raw;
322 - kernel_uint_t cgtime_raw; // cguest_time
323 -
324 - // these are rates
325 - kernel_uint_t minflt;
326 - kernel_uint_t cminflt;
327 - kernel_uint_t majflt;
328 - kernel_uint_t cmajflt;
329 - kernel_uint_t utime;
330 - kernel_uint_t stime;
331 - kernel_uint_t gtime;
332 - kernel_uint_t cutime;
333 - kernel_uint_t cstime;
334 - kernel_uint_t cgtime;
335 -
336 - // int64_t priority;
337 - // int64_t nice;
338 - int32_t num_threads;
339 - // int64_t itrealvalue;
340 - // kernel_uint_t collected_starttime;
341 - // kernel_uint_t vsize;
342 - // kernel_uint_t rss;
343 - // kernel_uint_t rsslim;
344 - // kernel_uint_t starcode;
345 - // kernel_uint_t endcode;
346 - // kernel_uint_t startstack;
347 - // kernel_uint_t kstkesp;
348 - // kernel_uint_t kstkeip;
349 - // uint64_t signal;
350 - // uint64_t blocked;
351 - // uint64_t sigignore;
352 - // uint64_t sigcatch;
353 - // uint64_t wchan;
354 - // uint64_t nswap;
355 - // uint64_t cnswap;
356 - // int32_t exit_signal;
357 - // int32_t processor;
358 - // uint32_t rt_priority;
359 - // uint32_t policy;
360 - // kernel_uint_t delayacct_blkio_ticks;
475 + struct pid_stat *parent;
476 + struct pid_stat *next;
477 + struct pid_stat *prev;
478
362 - uid_t uid;
363 - gid_t gid;
479 + struct target *target; // app_groups.conf targets
480
365 - kernel_uint_t status_voluntary_ctxt_switches_raw;
366 - kernel_uint_t status_nonvoluntary_ctxt_switches_raw;
367 -
368 - kernel_uint_t status_vmsize;
369 - kernel_uint_t status_vmrss;
370 - kernel_uint_t status_vmshared;
371 - kernel_uint_t status_rssfile;
372 - kernel_uint_t status_rssshmem;
373 - kernel_uint_t status_vmswap;
374 - kernel_uint_t status_voluntary_ctxt_switches;
375 - kernel_uint_t status_nonvoluntary_ctxt_switches;
376 -#ifndef __FreeBSD__
377 - ARL_BASE *status_arl;
481 +#if (PROCESSES_HAVE_UID == 1)
482 + struct target *uid_target; // uid based targets
483 +#endif
484 +#if (PROCESSES_HAVE_GID == 1)
485 + struct target *gid_target; // gid based targets
486 #endif
487
380 - kernel_uint_t io_logical_bytes_read_raw;
381 - kernel_uint_t io_logical_bytes_written_raw;
382 - kernel_uint_t io_read_calls_raw;
383 - kernel_uint_t io_write_calls_raw;
384 - kernel_uint_t io_storage_bytes_read_raw;
385 - kernel_uint_t io_storage_bytes_written_raw;
386 - kernel_uint_t io_cancelled_write_bytes_raw;
488 + STRING *comm; // the command name (short version)
489 + STRING *name; // a better name, or NULL
490 + STRING *cmdline; // the full command line (or on windows, the full pathname of the program)
491
388 - kernel_uint_t io_logical_bytes_read;
389 - kernel_uint_t io_logical_bytes_written;
390 - kernel_uint_t io_read_calls;
391 - kernel_uint_t io_write_calls;
392 - kernel_uint_t io_storage_bytes_read;
393 - kernel_uint_t io_storage_bytes_written;
394 - kernel_uint_t io_cancelled_write_bytes;
492 +#if defined(OS_WINDOWS)
493 + COUNTER_DATA perflib[PDF_MAX];
494 +#else
495 + kernel_uint_t raw[PDF_MAX];
496 +#endif
497
396 - kernel_uint_t uptime;
498 + kernel_uint_t values[PDF_MAX];
499
398 - struct pid_fd *fds; // array of fds it uses
399 - size_t fds_size; // the size of the fds array
500 +#if (PROCESSES_HAVE_UID == 1)
501 + uid_t uid;
502 +#endif
503 +#if (PROCESSES_HAVE_GID == 1)
504 + gid_t gid;
505 +#endif
506
507 +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
508 + uint32_t sortlist; // higher numbers = top on the process tree
509 + // each process gets a unique number (non-sequential though)
510 +#endif
511 +
512 +#if (PROCESSES_HAVE_FDS == 1)
513 struct openfds openfds;
514 +#if (PROCESSES_HAVE_PID_LIMITS == 1)
515 struct pid_limits limits;
403 -
516 NETDATA_DOUBLE openfds_limits_percent;
517 +#endif
518 + struct pid_fd *fds; // array of fds it uses
519 + uint32_t fds_size; // the size of the fds array
520 +#endif
521
406 - int sortlist; // higher numbers = top on the process tree
407 - // each process gets a unique number
522 + uint32_t children_count; // number of processes directly referencing this
523 + // it is absorbed by apps_groups.conf inheritance
524 + // don't rely on it for anything else.
525
409 - int children_count; // number of processes directly referencing this
410 - int keeploops; // increases by 1 every time keep is 1 and updated 0
526 + uint32_t keeploops; // increases by 1 every time keep is 1 and updated 0
527
528 PID_LOG log_thrown;
529
414 - bool keep; // true when we need to keep this process in memory even after it exited
415 - bool updated; // true when the process is currently running
416 - bool merged; // true when it has been merged to its parent
417 - bool read; // true when we have already read this process for this iteration
418 - bool matched_by_config;
530 + bool read:1; // true when we have already read this process for this iteration
531 + bool updated:1; // true when the process is currently running
532 + bool merged:1; // true when it has been merged to its parent
533 + bool keep:1; // true when we need to keep this process in memory even after it exited
534
420 - struct target *target; // app_groups.conf targets
421 - struct target *user_target; // uid based targets
422 - struct target *group_target; // gid based targets
535 + bool matched_by_config:1;
536 +
537 +#if (PROCESSES_HAVE_STATE == 1)
538 + char state;
539 +#endif
540 +
541 +#if defined(OS_WINDOWS)
542 + bool got_info:1;
543 + bool assigned_to_target:1;
544 + bool initialized:1;
545 +#endif
546
547 usec_t stat_collected_usec;
548 usec_t last_stat_collected_usec;
@@ -428,27 +551,30 @@ struct pid_stat {
551 usec_t last_io_collected_usec;
552 usec_t last_limits_collected_usec;
553
554 +#if defined(OS_LINUX)
555 + ARL_BASE *status_arl;
556 char *fds_dirname; // the full directory name in /proc/PID/fd
432 -
557 char *stat_filename;
558 char *status_filename;
559 char *io_filename;
560 char *cmdline_filename;
561 char *limits_filename;
438 -
439 - struct pid_stat *parent;
440 - struct pid_stat *prev;
441 - struct pid_stat *next;
562 +#endif
563 };
564
565 // ----------------------------------------------------------------------------
566
567 +#if (PROCESSES_HAVE_UID == 1) || (PROCESSES_HAVE_GID == 1)
568 struct user_or_group_id {
569 avl_t avl;
570
571 union {
572 +#if (PROCESSES_HAVE_UID == 1)
573 uid_t uid;
574 +#endif
575 +#if (PROCESSES_HAVE_GID == 1)
576 gid_t gid;
577 +#endif
578 } id;
579
580 char *name;
@@ -457,39 +583,9 @@ struct user_or_group_id {
583
584 struct user_or_group_id * next;
585 };
460 -
461 -extern struct target
462 - *apps_groups_default_target,
463 - *apps_groups_root_target,
464 - *users_root_target,
465 - *groups_root_target;
466 -
467 -extern struct pid_stat *root_of_pids;
468 -
469 -extern int update_every;
470 -extern unsigned int time_factor;
471 -extern kernel_uint_t MemTotal;
472 -
473 -#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
474 -extern pid_t *all_pids_sortlist;
586 #endif
587
477 -#define APPS_PLUGIN_PROCESSES_FUNCTION_DESCRIPTION "Detailed information on the currently running processes."
478 -
479 -void function_processes(const char *transaction, char *function,
480 - usec_t *stop_monotonic_ut __maybe_unused, bool *cancelled __maybe_unused,
481 - BUFFER *payload __maybe_unused, HTTP_ACCESS access,
482 - const char *source __maybe_unused, void *data __maybe_unused);
483 -
484 -struct target *find_target_by_name(struct target *base, const char *name);
485 -
486 -struct target *get_users_target(uid_t uid);
487 -struct target *get_groups_target(gid_t gid);
488 -int read_apps_groups_conf(const char *path, const char *file);
489 -
490 -void users_and_groups_init(void);
491 -struct user_or_group_id *user_id_find(struct user_or_group_id *user_id_to_find);
492 -struct user_or_group_id *group_id_find(struct user_or_group_id *group_id_to_find);
588 +extern int update_every;
589
590 // ----------------------------------------------------------------------------
591 // debugging
@@ -515,46 +611,141 @@ static inline void debug_log_dummy(void) {}
611 #define debug_log(fmt, args...) debug_log_dummy()
612
613 #endif
518 -int managed_log(struct pid_stat *p, PID_LOG log, int status);
614 +bool managed_log(struct pid_stat *p, PID_LOG log, bool status);
615
616 // ----------------------------------------------------------------------------
617 // macro to calculate the incremental rate of a value
618 // each parameter is accessed only ONCE - so it is safe to pass function calls
619 // or other macros as parameters
620
525 -#define incremental_rate(rate_variable, last_kernel_variable, new_kernel_value, collected_usec, last_collected_usec) do { \
621 +#define incremental_rate(rate_variable, last_kernel_variable, new_kernel_value, collected_usec, last_collected_usec, multiplier) do { \
622 kernel_uint_t _new_tmp = new_kernel_value; \
527 - (rate_variable) = (_new_tmp - (last_kernel_variable)) * (USEC_PER_SEC * RATES_DETAIL) / ((collected_usec) - (last_collected_usec)); \
623 + (rate_variable) = (_new_tmp - (last_kernel_variable)) * (USEC_PER_SEC * multiplier) / ((collected_usec) - (last_collected_usec)); \
624 (last_kernel_variable) = _new_tmp; \
625 } while(0)
626
627 // the same macro for struct pid members
532 -#define pid_incremental_rate(type, var, value) \
533 - incremental_rate(var, var##_raw, value, p->type##_collected_usec, p->last_##type##_collected_usec)
628 +#define pid_incremental_rate(type, idx, value) \
629 + incremental_rate(p->values[idx], p->raw[idx], value, p->type##_collected_usec, p->last_##type##_collected_usec, RATES_DETAIL)
630
535 -int read_proc_pid_stat(struct pid_stat *p, void *ptr);
536 -int read_proc_pid_limits(struct pid_stat *p, void *ptr);
537 -int read_proc_pid_status(struct pid_stat *p, void *ptr);
538 -int read_proc_pid_cmdline(struct pid_stat *p);
539 -int read_proc_pid_io(struct pid_stat *p, void *ptr);
540 -int read_pid_file_descriptors(struct pid_stat *p, void *ptr);
541 -int read_global_time(void);
542 -void get_MemTotal(void);
631 +#define pid_incremental_cpu(type, idx, value) \
632 + incremental_rate(p->values[idx], p->raw[idx], value, p->type##_collected_usec, p->last_##type##_collected_usec, CPU_TO_NANOSECONDCORES)
633
544 -bool collect_data_for_all_pids(void);
545 -void cleanup_exited_pids(void);
634 +void apps_orchestrators_and_aggregators_init(void);
635 +void apps_users_and_groups_init(void);
636 +void apps_pids_init(void);
637 +
638 +#if (PROCESSES_HAVE_CMDLINE == 1)
639 +int read_proc_pid_cmdline(struct pid_stat *p);
640 +#endif
641
642 +#if (PROCESSES_HAVE_FDS == 1)
643 void clear_pid_fd(struct pid_fd *pfd);
644 void file_descriptor_not_used(int id);
645 void init_pid_fds(struct pid_stat *p, size_t first, size_t size);
646 void aggregate_pid_fds_on_targets(struct pid_stat *p);
647 +int read_pid_file_descriptors(struct pid_stat *p, void *ptr);
648 +void make_all_pid_fds_negative(struct pid_stat *p);
649 +uint32_t file_descriptor_find_or_add(const char *name, uint32_t hash);
650 +#endif
651 +
652 +// --------------------------------------------------------------------------------------------------------------------
653 +// data collection management
654 +
655 +bool collect_data_for_all_pids(void);
656 +
657 +void pid_collection_started(struct pid_stat *p);
658 +void pid_collection_failed(struct pid_stat *p);
659 +void pid_collection_completed(struct pid_stat *p);
660 +
661 +#if (INCREMENTAL_DATA_COLLECTION == 1)
662 +bool collect_parents_before_children(void);
663 +int incrementally_collect_data_for_pid(pid_t pid, void *ptr);
664 +int incrementally_collect_data_for_pid_stat(struct pid_stat *p, void *ptr);
665 +#endif
666 +
667 +// --------------------------------------------------------------------------------------------------------------------
668 +// pid management
669 +
670 +struct pid_stat *root_of_pids(void);
671 +size_t all_pids_count(void);
672 +
673 +struct pid_stat *get_or_allocate_pid_entry(pid_t pid);
674 +struct pid_stat *find_pid_entry(pid_t pid);
675 +void del_pid_entry(pid_t pid);
676 +void update_pid_comm(struct pid_stat *p, const char *comm);
677 +
678 +
679 +// --------------------------------------------------------------------------------------------------------------------
680 +// targets management
681 +
682 +struct target *find_target_by_name(struct target *base, const char *name);
683 +struct target *get_tree_target(struct pid_stat *p);
684 +
685 +void aggregate_processes_to_targets(void);
686 +
687 +#if (PROCESSES_HAVE_UID == 1)
688 +extern struct target *users_root_target;
689 +struct target *get_uid_target(uid_t uid);
690 +struct user_or_group_id *user_id_find(struct user_or_group_id *user_id_to_find);
691 +#endif
692 +
693 +#if (PROCESSES_HAVE_GID == 1)
694 +extern struct target *groups_root_target;
695 +struct target *get_gid_target(gid_t gid);
696 +struct user_or_group_id *group_id_find(struct user_or_group_id *group_id_to_find);
697 +#endif
698 +
699 +extern struct target *apps_groups_root_target;
700 +int read_apps_groups_conf(const char *path, const char *file);
701 +
702 +// --------------------------------------------------------------------------------------------------------------------
703 +// output
704
552 -void send_proc_states_count(usec_t dt);
705 void send_charts_updates_to_netdata(struct target *root, const char *type, const char *lbl_name, const char *title);
706 void send_collected_data_to_netdata(struct target *root, const char *type, usec_t dt);
707 void send_resource_usage_to_netdata(usec_t dt);
708
557 -void pids_init(void);
558 -struct pid_stat *find_pid_entry(pid_t pid);
709 +#if (PROCESSES_HAVE_STATE == 1)
710 +void send_proc_states_count(usec_t dt);
711 +#endif
712 +
713 +#define APPS_PLUGIN_PROCESSES_FUNCTION_DESCRIPTION "Detailed information on the currently running processes."
714 +void function_processes(const char *transaction, char *function,
715 + usec_t *stop_monotonic_ut __maybe_unused, bool *cancelled __maybe_unused,
716 + BUFFER *payload __maybe_unused, HTTP_ACCESS access,
717 + const char *source __maybe_unused, void *data __maybe_unused);
718 +
719 +// --------------------------------------------------------------------------------------------------------------------
720 +// operating system functions
721 +
722 +// one time initialization per operating system
723 +void OS_FUNCTION(apps_os_init)(void);
724 +
725 +// collect all the available information for all processes running
726 +bool OS_FUNCTION(apps_os_collect_all_pids)(void);
727 +
728 +bool OS_FUNCTION(apps_os_read_pid_status)(struct pid_stat *p, void *ptr);
729 +bool OS_FUNCTION(apps_os_read_pid_stat)(struct pid_stat *p, void *ptr);
730 +bool OS_FUNCTION(apps_os_read_pid_io)(struct pid_stat *p, void *ptr);
731 +
732 +#if (PROCESSES_HAVE_PID_LIMITS == 1)
733 +bool OS_FUNCTION(apps_os_read_pid_limits)(struct pid_stat *p, void *ptr);
734 +#endif
735 +
736 +#if (PROCESSES_HAVE_CMDLINE == 1)
737 +bool OS_FUNCTION(apps_os_get_pid_cmdline)(struct pid_stat *p, char *cmdline, size_t bytes);
738 +#endif
739 +
740 +#if (PROCESSES_HAVE_FDS == 1)
741 +bool OS_FUNCTION(apps_os_read_pid_fds)(struct pid_stat *p, void *ptr);
742 +#endif
743 +
744 +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
745 +bool OS_FUNCTION(apps_os_read_global_cpu_utilization)(void);
746 +#endif
747 +
748 +// return the total physical memory of the system, in bytes
749 +uint64_t OS_FUNCTION(apps_os_get_total_memory)(void);
750
751 #endif //NETDATA_APPS_PLUGIN_H
src/collectors/apps.plugin/apps_proc_meminfo.c deleted
-68
@@ -1,68 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "apps_plugin.h"
4 -
5 -kernel_uint_t MemTotal = 0;
6 -
7 -#ifdef __FreeBSD__
8 -static inline bool get_MemTotal_per_os(void) {
9 - int mib[2] = {CTL_HW, HW_PHYSMEM};
10 - size_t size = sizeof(MemTotal);
11 - if (sysctl(mib, 2, &MemTotal, &size, NULL, 0) == -1) {
12 - netdata_log_error("Failed to get total memory using sysctl");
13 - return false;
14 - }
15 - // FreeBSD returns bytes; convert to kB
16 - MemTotal /= 1024;
17 - return true;
18 -}
19 -#endif // __FreeBSD__
20 -
21 -#ifdef __APPLE__
22 -static inline bool get_MemTotal_per_os(void) {
23 - int mib[2] = {CTL_HW, HW_MEMSIZE};
24 - size_t size = sizeof(MemTotal);
25 - if (sysctl(mib, 2, &MemTotal, &size, NULL, 0) == -1) {
26 - netdata_log_error("Failed to get total memory using sysctl");
27 - return false;
28 - }
29 - // MacOS returns bytes; convert to kB
30 - MemTotal /= 1024;
31 - return true;
32 -}
33 -#endif // __APPLE__
34 -
35 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
36 -static inline bool get_MemTotal_per_os(void) {
37 - char filename[FILENAME_MAX + 1];
38 - snprintfz(filename, FILENAME_MAX, "%s/proc/meminfo", netdata_configured_host_prefix);
39 -
40 - procfile *ff = procfile_open(filename, ": \t", PROCFILE_FLAG_DEFAULT);
41 - if(!ff)
42 - return false;
43 -
44 - ff = procfile_readall(ff);
45 - if(!ff)
46 - return false;
47 -
48 - size_t line, lines = procfile_lines(ff);
49 -
50 - for(line = 0; line < lines ;line++) {
51 - size_t words = procfile_linewords(ff, line);
52 - if(words == 3 && strcmp(procfile_lineword(ff, line, 0), "MemTotal") == 0 && strcmp(procfile_lineword(ff, line, 2), "kB") == 0) {
53 - kernel_uint_t n = str2ull(procfile_lineword(ff, line, 1), NULL);
54 - if(n) MemTotal = n;
55 - break;
56 - }
57 - }
58 -
59 - procfile_close(ff);
60 -
61 - return true;
62 -}
63 -#endif
64 -
65 -void get_MemTotal(void) {
66 - if(!get_MemTotal_per_os())
67 - MemTotal = 0;
68 -}
src/collectors/apps.plugin/apps_proc_pid_cmdline.c deleted
-130
@@ -1,130 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "apps_plugin.h"
4 -
5 -#ifdef __APPLE__
6 -bool get_cmdline_per_os(struct pid_stat *p, char *cmdline, size_t maxBytes) {
7 - int mib[3] = {CTL_KERN, KERN_PROCARGS2, p->pid};
8 - static char *args = NULL;
9 - static size_t size = 0;
10 -
11 - size_t new_size;
12 - if (sysctl(mib, 3, NULL, &new_size, NULL, 0) == -1) {
13 - return false;
14 - }
15 -
16 - if (new_size > size) {
17 - if (args)
18 - freez(args);
19 -
20 - args = (char *)mallocz(new_size);
21 - size = new_size;
22 - }
23 -
24 - memset(cmdline, 0, new_size < maxBytes ? new_size : maxBytes);
25 -
26 - size_t used_size = size;
27 - if (sysctl(mib, 3, args, &used_size, NULL, 0) == -1)
28 - return false;
29 -
30 - int argc;
31 - memcpy(&argc, args, sizeof(argc));
32 - char *ptr = args + sizeof(argc);
33 - used_size -= sizeof(argc);
34 -
35 - // Skip the executable path
36 - while (*ptr && used_size > 0) {
37 - ptr++;
38 - used_size--;
39 - }
40 -
41 - // Copy only the arguments to the cmdline buffer, skipping the environment variables
42 - size_t i = 0, copied_args = 0;
43 - bool inArg = false;
44 - for (; used_size > 0 && i < maxBytes - 1 && copied_args < argc; --used_size, ++ptr) {
45 - if (*ptr == '\0') {
46 - if (inArg) {
47 - cmdline[i++] = ' '; // Replace nulls between arguments with spaces
48 - inArg = false;
49 - copied_args++;
50 - }
51 - } else {
52 - cmdline[i++] = *ptr;
53 - inArg = true;
54 - }
55 - }
56 -
57 - if (i > 0 && cmdline[i - 1] == ' ')
58 - i--; // Remove the trailing space if present
59 -
60 - cmdline[i] = '\0'; // Null-terminate the string
61 -
62 - return true;
63 -}
64 -#endif // __APPLE__
65 -
66 -#if defined(__FreeBSD__)
67 -static inline bool get_cmdline_per_os(struct pid_stat *p, char *cmdline, size_t bytes) {
68 - size_t i, b = bytes - 1;
69 - int mib[4];
70 -
71 - mib[0] = CTL_KERN;
72 - mib[1] = KERN_PROC;
73 - mib[2] = KERN_PROC_ARGS;
74 - mib[3] = p->pid;
75 - if (unlikely(sysctl(mib, 4, cmdline, &b, NULL, 0)))
76 - return false;
77 -
78 - cmdline[b] = '\0';
79 - for(i = 0; i < b ; i++)
80 - if(unlikely(!cmdline[i])) cmdline[i] = ' ';
81 -
82 - return true;
83 -}
84 -#endif // __FreeBSD__
85 -
86 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
87 -static inline bool get_cmdline_per_os(struct pid_stat *p, char *cmdline, size_t bytes) {
88 - if(unlikely(!p->cmdline_filename)) {
89 - char filename[FILENAME_MAX];
90 - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", netdata_configured_host_prefix, p->pid);
91 - p->cmdline_filename = strdupz(filename);
92 - }
93 -
94 - int fd = open(p->cmdline_filename, procfile_open_flags, 0666);
95 - if(unlikely(fd == -1))
96 - return false;
97 -
98 - ssize_t i, b = read(fd, cmdline, bytes - 1);
99 - close(fd);
100 -
101 - if(unlikely(b < 0))
102 - return false;
103 -
104 - cmdline[b] = '\0';
105 - for(i = 0; i < b ; i++)
106 - if(unlikely(!cmdline[i])) cmdline[i] = ' ';
107 -
108 - return true;
109 -}
110 -#endif // !__FreeBSD__ !__APPLE__
111 -
112 -int read_proc_pid_cmdline(struct pid_stat *p) {
113 - static char cmdline[MAX_CMDLINE];
114 -
115 - if(unlikely(!get_cmdline_per_os(p, cmdline, sizeof(cmdline))))
116 - goto cleanup;
117 -
118 - if(p->cmdline) freez(p->cmdline);
119 - p->cmdline = strdupz(cmdline);
120 -
121 - debug_log("Read file '%s' contents: %s", p->cmdline_filename, p->cmdline);
122 -
123 - return 1;
124 -
125 -cleanup:
126 - // copy the command to the command line
127 - if(p->cmdline) freez(p->cmdline);
128 - p->cmdline = strdupz(p->comm);
129 - return 0;
130 -}
src/collectors/apps.plugin/apps_proc_pid_fd.c deleted
-753
@@ -1,753 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "apps_plugin.h"
4 -
5 -// ----------------------------------------------------------------------------
6 -// file descriptor
7 -//
8 -// this is used to keep a global list of all open files of the system.
9 -// it is needed in order to calculate the unique files processes have open.
10 -
11 -#define FILE_DESCRIPTORS_INCREASE_STEP 100
12 -
13 -// types for struct file_descriptor->type
14 -typedef enum fd_filetype {
15 - FILETYPE_OTHER,
16 - FILETYPE_FILE,
17 - FILETYPE_PIPE,
18 - FILETYPE_SOCKET,
19 - FILETYPE_INOTIFY,
20 - FILETYPE_EVENTFD,
21 - FILETYPE_EVENTPOLL,
22 - FILETYPE_TIMERFD,
23 - FILETYPE_SIGNALFD
24 -} FD_FILETYPE;
25 -
26 -struct file_descriptor {
27 - avl_t avl;
28 -
29 -#ifdef NETDATA_INTERNAL_CHECKS
30 - uint32_t magic;
31 -#endif /* NETDATA_INTERNAL_CHECKS */
32 -
33 - const char *name;
34 - uint32_t hash;
35 -
36 - FD_FILETYPE type;
37 - int count;
38 - int pos;
39 -} *all_files = NULL;
40 -
41 -// ----------------------------------------------------------------------------
42 -
43 -static inline void reallocate_target_fds(struct target *w) {
44 - if(unlikely(!w))
45 - return;
46 -
47 - if(unlikely(!w->target_fds || w->target_fds_size < all_files_size)) {
48 - w->target_fds = reallocz(w->target_fds, sizeof(int) * all_files_size);
49 - memset(&w->target_fds[w->target_fds_size], 0, sizeof(int) * (all_files_size - w->target_fds_size));
50 - w->target_fds_size = all_files_size;
51 - }
52 -}
53 -
54 -static void aggregage_fd_type_on_openfds(FD_FILETYPE type, struct openfds *openfds) {
55 - switch(type) {
56 - case FILETYPE_FILE:
57 - openfds->files++;
58 - break;
59 -
60 - case FILETYPE_PIPE:
61 - openfds->pipes++;
62 - break;
63 -
64 - case FILETYPE_SOCKET:
65 - openfds->sockets++;
66 - break;
67 -
68 - case FILETYPE_INOTIFY:
69 - openfds->inotifies++;
70 - break;
71 -
72 - case FILETYPE_EVENTFD:
73 - openfds->eventfds++;
74 - break;
75 -
76 - case FILETYPE_TIMERFD:
77 - openfds->timerfds++;
78 - break;
79 -
80 - case FILETYPE_SIGNALFD:
81 - openfds->signalfds++;
82 - break;
83 -
84 - case FILETYPE_EVENTPOLL:
85 - openfds->eventpolls++;
86 - break;
87 -
88 - case FILETYPE_OTHER:
89 - openfds->other++;
90 - break;
91 - }
92 -}
93 -
94 -static inline void aggregate_fd_on_target(int fd, struct target *w) {
95 - if(unlikely(!w))
96 - return;
97 -
98 - if(unlikely(w->target_fds[fd])) {
99 - // it is already aggregated
100 - // just increase its usage counter
101 - w->target_fds[fd]++;
102 - return;
103 - }
104 -
105 - // increase its usage counter
106 - // so that we will not add it again
107 - w->target_fds[fd]++;
108 -
109 - aggregage_fd_type_on_openfds(all_files[fd].type, &w->openfds);
110 -}
111 -
112 -void aggregate_pid_fds_on_targets(struct pid_stat *p) {
113 -
114 - if(unlikely(!p->updated)) {
115 - // the process is not running
116 - return;
117 - }
118 -
119 - struct target *w = p->target, *u = p->user_target, *g = p->group_target;
120 -
121 - reallocate_target_fds(w);
122 - reallocate_target_fds(u);
123 - reallocate_target_fds(g);
124 -
125 - p->openfds.files = 0;
126 - p->openfds.pipes = 0;
127 - p->openfds.sockets = 0;
128 - p->openfds.inotifies = 0;
129 - p->openfds.eventfds = 0;
130 - p->openfds.timerfds = 0;
131 - p->openfds.signalfds = 0;
132 - p->openfds.eventpolls = 0;
133 - p->openfds.other = 0;
134 -
135 - long currentfds = 0;
136 - size_t c, size = p->fds_size;
137 - struct pid_fd *fds = p->fds;
138 - for(c = 0; c < size ;c++) {
139 - int fd = fds[c].fd;
140 -
141 - if(likely(fd <= 0 || fd >= all_files_size))
142 - continue;
143 -
144 - currentfds++;
145 - aggregage_fd_type_on_openfds(all_files[fd].type, &p->openfds);
146 -
147 - aggregate_fd_on_target(fd, w);
148 - aggregate_fd_on_target(fd, u);
149 - aggregate_fd_on_target(fd, g);
150 - }
151 -}
152 -
153 -// ----------------------------------------------------------------------------
154 -
155 -int file_descriptor_compare(void* a, void* b) {
156 -#ifdef NETDATA_INTERNAL_CHECKS
157 - if(((struct file_descriptor *)a)->magic != 0x0BADCAFE || ((struct file_descriptor *)b)->magic != 0x0BADCAFE)
158 - netdata_log_error("Corrupted index data detected. Please report this.");
159 -#endif /* NETDATA_INTERNAL_CHECKS */
160 -
161 - if(((struct file_descriptor *)a)->hash < ((struct file_descriptor *)b)->hash)
162 - return -1;
163 -
164 - else if(((struct file_descriptor *)a)->hash > ((struct file_descriptor *)b)->hash)
165 - return 1;
166 -
167 - else
168 - return strcmp(((struct file_descriptor *)a)->name, ((struct file_descriptor *)b)->name);
169 -}
170 -
171 -// int file_descriptor_iterator(avl_t *a) { if(a) {}; return 0; }
172 -
173 -avl_tree_type all_files_index = {
174 - NULL,
175 - file_descriptor_compare
176 -};
177 -
178 -static struct file_descriptor *file_descriptor_find(const char *name, uint32_t hash) {
179 - struct file_descriptor tmp;
180 - tmp.hash = (hash)?hash:simple_hash(name);
181 - tmp.name = name;
182 - tmp.count = 0;
183 - tmp.pos = 0;
184 -#ifdef NETDATA_INTERNAL_CHECKS
185 - tmp.magic = 0x0BADCAFE;
186 -#endif /* NETDATA_INTERNAL_CHECKS */
187 -
188 - return (struct file_descriptor *)avl_search(&all_files_index, (avl_t *) &tmp);
189 -}
190 -
191 -#define file_descriptor_add(fd) avl_insert(&all_files_index, (avl_t *)(fd))
192 -#define file_descriptor_remove(fd) avl_remove(&all_files_index, (avl_t *)(fd))
193 -
194 -// ----------------------------------------------------------------------------
195 -
196 -void file_descriptor_not_used(int id) {
197 - if(id > 0 && id < all_files_size) {
198 -
199 -#ifdef NETDATA_INTERNAL_CHECKS
200 - if(all_files[id].magic != 0x0BADCAFE) {
201 - netdata_log_error("Ignoring request to remove empty file id %d.", id);
202 - return;
203 - }
204 -#endif /* NETDATA_INTERNAL_CHECKS */
205 -
206 - debug_log("decreasing slot %d (count = %d).", id, all_files[id].count);
207 -
208 - if(all_files[id].count > 0) {
209 - all_files[id].count--;
210 -
211 - if(!all_files[id].count) {
212 - debug_log(" >> slot %d is empty.", id);
213 -
214 - if(unlikely(file_descriptor_remove(&all_files[id]) != (void *)&all_files[id]))
215 - netdata_log_error("INTERNAL ERROR: removal of unused fd from index, removed a different fd");
216 -
217 -#ifdef NETDATA_INTERNAL_CHECKS
218 - all_files[id].magic = 0x00000000;
219 -#endif /* NETDATA_INTERNAL_CHECKS */
220 - all_files_len--;
221 - }
222 - }
223 - else
224 - netdata_log_error("Request to decrease counter of fd %d (%s), while the use counter is 0",
225 - id,
226 - all_files[id].name);
227 - }
228 - else
229 - netdata_log_error("Request to decrease counter of fd %d, which is outside the array size (1 to %d)",
230 - id,
231 - all_files_size);
232 -}
233 -
234 -static inline void all_files_grow() {
235 - void *old = all_files;
236 - int i;
237 -
238 - // there is no empty slot
239 - debug_log("extending fd array to %d entries", all_files_size + FILE_DESCRIPTORS_INCREASE_STEP);
240 -
241 - all_files = reallocz(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor));
242 -
243 - // if the address changed, we have to rebuild the index
244 - // since all pointers are now invalid
245 -
246 - if(unlikely(old && old != (void *)all_files)) {
247 - debug_log(" >> re-indexing.");
248 -
249 - all_files_index.root = NULL;
250 - for(i = 0; i < all_files_size; i++) {
251 - if(!all_files[i].count) continue;
252 - if(unlikely(file_descriptor_add(&all_files[i]) != (void *)&all_files[i]))
253 - netdata_log_error("INTERNAL ERROR: duplicate indexing of fd during realloc.");
254 - }
255 -
256 - debug_log(" >> re-indexing done.");
257 - }
258 -
259 - // initialize the newly added entries
260 -
261 - for(i = all_files_size; i < (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); i++) {
262 - all_files[i].count = 0;
263 - all_files[i].name = NULL;
264 -#ifdef NETDATA_INTERNAL_CHECKS
265 - all_files[i].magic = 0x00000000;
266 -#endif /* NETDATA_INTERNAL_CHECKS */
267 - all_files[i].pos = i;
268 - }
269 -
270 - if(unlikely(!all_files_size)) all_files_len = 1;
271 - all_files_size += FILE_DESCRIPTORS_INCREASE_STEP;
272 -}
273 -
274 -static inline int file_descriptor_set_on_empty_slot(const char *name, uint32_t hash, FD_FILETYPE type) {
275 - // check we have enough memory to add it
276 - if(!all_files || all_files_len == all_files_size)
277 - all_files_grow();
278 -
279 - debug_log(" >> searching for empty slot.");
280 -
281 - // search for an empty slot
282 -
283 - static int last_pos = 0;
284 - int i, c;
285 - for(i = 0, c = last_pos ; i < all_files_size ; i++, c++) {
286 - if(c >= all_files_size) c = 0;
287 - if(c == 0) continue;
288 -
289 - if(!all_files[c].count) {
290 - debug_log(" >> Examining slot %d.", c);
291 -
292 -#ifdef NETDATA_INTERNAL_CHECKS
293 - if(all_files[c].magic == 0x0BADCAFE && all_files[c].name && file_descriptor_find(all_files[c].name, all_files[c].hash))
294 - netdata_log_error("fd on position %d is not cleared properly. It still has %s in it.", c, all_files[c].name);
295 -#endif /* NETDATA_INTERNAL_CHECKS */
296 -
297 - debug_log(" >> %s fd position %d for %s (last name: %s)", all_files[c].name?"re-using":"using", c, name, all_files[c].name);
298 -
299 - freez((void *)all_files[c].name);
300 - all_files[c].name = NULL;
301 - last_pos = c;
302 - break;
303 - }
304 - }
305 -
306 - all_files_len++;
307 -
308 - if(i == all_files_size) {
309 - fatal("We should find an empty slot, but there isn't any");
310 - exit(1);
311 - }
312 - // else we have an empty slot in 'c'
313 -
314 - debug_log(" >> updating slot %d.", c);
315 -
316 - all_files[c].name = strdupz(name);
317 - all_files[c].hash = hash;
318 - all_files[c].type = type;
319 - all_files[c].pos = c;
320 - all_files[c].count = 1;
321 -#ifdef NETDATA_INTERNAL_CHECKS
322 - all_files[c].magic = 0x0BADCAFE;
323 -#endif /* NETDATA_INTERNAL_CHECKS */
324 - if(unlikely(file_descriptor_add(&all_files[c]) != (void *)&all_files[c]))
325 - netdata_log_error("INTERNAL ERROR: duplicate indexing of fd.");
326 -
327 - debug_log("using fd position %d (name: %s)", c, all_files[c].name);
328 -
329 - return c;
330 -}
331 -
332 -static inline int file_descriptor_find_or_add(const char *name, uint32_t hash) {
333 - if(unlikely(!hash))
334 - hash = simple_hash(name);
335 -
336 - debug_log("adding or finding name '%s' with hash %u", name, hash);
337 -
338 - struct file_descriptor *fd = file_descriptor_find(name, hash);
339 - if(fd) {
340 - // found
341 - debug_log(" >> found on slot %d", fd->pos);
342 -
343 - fd->count++;
344 - return fd->pos;
345 - }
346 - // not found
347 -
348 - FD_FILETYPE type;
349 - if(likely(name[0] == '/')) type = FILETYPE_FILE;
350 - else if(likely(strncmp(name, "pipe:", 5) == 0)) type = FILETYPE_PIPE;
351 - else if(likely(strncmp(name, "socket:", 7) == 0)) type = FILETYPE_SOCKET;
352 - else if(likely(strncmp(name, "anon_inode:", 11) == 0)) {
353 - const char *t = &name[11];
354 -
355 - if(strcmp(t, "inotify") == 0) type = FILETYPE_INOTIFY;
356 - else if(strcmp(t, "[eventfd]") == 0) type = FILETYPE_EVENTFD;
357 - else if(strcmp(t, "[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
358 - else if(strcmp(t, "[timerfd]") == 0) type = FILETYPE_TIMERFD;
359 - else if(strcmp(t, "[signalfd]") == 0) type = FILETYPE_SIGNALFD;
360 - else {
361 - debug_log("UNKNOWN anonymous inode: %s", name);
362 - type = FILETYPE_OTHER;
363 - }
364 - }
365 - else if(likely(strcmp(name, "inotify") == 0)) type = FILETYPE_INOTIFY;
366 - else {
367 - debug_log("UNKNOWN linkname: %s", name);
368 - type = FILETYPE_OTHER;
369 - }
370 -
371 - return file_descriptor_set_on_empty_slot(name, hash, type);
372 -}
373 -
374 -void clear_pid_fd(struct pid_fd *pfd) {
375 - pfd->fd = 0;
376 -
377 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
378 - pfd->link_hash = 0;
379 - pfd->inode = 0;
380 - pfd->cache_iterations_counter = 0;
381 - pfd->cache_iterations_reset = 0;
382 -#endif
383 -}
384 -
385 -static inline void make_all_pid_fds_negative(struct pid_stat *p) {
386 - struct pid_fd *pfd = p->fds, *pfdend = &p->fds[p->fds_size];
387 - while(pfd < pfdend) {
388 - pfd->fd = -(pfd->fd);
389 - pfd++;
390 - }
391 -}
392 -
393 -static inline void cleanup_negative_pid_fds(struct pid_stat *p) {
394 - struct pid_fd *pfd = p->fds, *pfdend = &p->fds[p->fds_size];
395 -
396 - while(pfd < pfdend) {
397 - int fd = pfd->fd;
398 -
399 - if(unlikely(fd < 0)) {
400 - file_descriptor_not_used(-(fd));
401 - clear_pid_fd(pfd);
402 - }
403 -
404 - pfd++;
405 - }
406 -}
407 -
408 -void init_pid_fds(struct pid_stat *p, size_t first, size_t size) {
409 - struct pid_fd *pfd = &p->fds[first], *pfdend = &p->fds[first + size];
410 -
411 - while(pfd < pfdend) {
412 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
413 - pfd->filename = NULL;
414 -#endif
415 - clear_pid_fd(pfd);
416 - pfd++;
417 - }
418 -}
419 -
420 -#ifdef __APPLE__
421 -static bool read_pid_file_descriptors_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
422 - static struct proc_fdinfo *fds = NULL;
423 - static int fdsCapacity = 0;
424 -
425 - int bufferSize = proc_pidinfo(p->pid, PROC_PIDLISTFDS, 0, NULL, 0);
426 - if (bufferSize <= 0) {
427 - netdata_log_error("Failed to get the size of file descriptors for PID %d", p->pid);
428 - return false;
429 - }
430 -
431 - // Resize buffer if necessary
432 - if (bufferSize > fdsCapacity) {
433 - if(fds)
434 - freez(fds);
435 -
436 - fds = mallocz(bufferSize);
437 - fdsCapacity = bufferSize;
438 - }
439 -
440 - int num_fds = proc_pidinfo(p->pid, PROC_PIDLISTFDS, 0, fds, bufferSize) / PROC_PIDLISTFD_SIZE;
441 - if (num_fds <= 0) {
442 - netdata_log_error("Failed to get the file descriptors for PID %d", p->pid);
443 - return false;
444 - }
445 -
446 - for (int i = 0; i < num_fds; i++) {
447 - switch (fds[i].proc_fdtype) {
448 - case PROX_FDTYPE_VNODE: {
449 - struct vnode_fdinfowithpath vi;
450 - if (proc_pidfdinfo(p->pid, fds[i].proc_fd, PROC_PIDFDVNODEPATHINFO, &vi, sizeof(vi)) > 0)
451 - p->openfds.files++;
452 - else
453 - p->openfds.other++;
454 -
455 - break;
456 - }
457 - case PROX_FDTYPE_SOCKET: {
458 - p->openfds.sockets++;
459 - break;
460 - }
461 - case PROX_FDTYPE_PIPE: {
462 - p->openfds.pipes++;
463 - break;
464 - }
465 -
466 - default:
467 - p->openfds.other++;
468 - break;
469 - }
470 - }
471 -
472 - return true;
473 -}
474 -#endif // __APPLE__
475 -
476 -#if defined(__FreeBSD__)
477 -static bool read_pid_file_descriptors_per_os(struct pid_stat *p, void *ptr) {
478 - int mib[4];
479 - size_t size;
480 - struct kinfo_file *fds;
481 - static char *fdsbuf;
482 - char *bfdsbuf, *efdsbuf;
483 - char fdsname[FILENAME_MAX + 1];
484 -#define SHM_FORMAT_LEN 31 // format: 21 + size: 10
485 - char shm_name[FILENAME_MAX - SHM_FORMAT_LEN + 1];
486 -
487 - // we make all pid fds negative, so that
488 - // we can detect unused file descriptors
489 - // at the end, to free them
490 - make_all_pid_fds_negative(p);
491 -
492 - mib[0] = CTL_KERN;
493 - mib[1] = KERN_PROC;
494 - mib[2] = KERN_PROC_FILEDESC;
495 - mib[3] = p->pid;
496 -
497 - if (unlikely(sysctl(mib, 4, NULL, &size, NULL, 0))) {
498 - netdata_log_error("sysctl error: Can't get file descriptors data size for pid %d", p->pid);
499 - return false;
500 - }
501 - if (likely(size > 0))
502 - fdsbuf = reallocz(fdsbuf, size);
503 - if (unlikely(sysctl(mib, 4, fdsbuf, &size, NULL, 0))) {
504 - netdata_log_error("sysctl error: Can't get file descriptors data for pid %d", p->pid);
505 - return false;
506 - }
507 -
508 - bfdsbuf = fdsbuf;
509 - efdsbuf = fdsbuf + size;
510 - while (bfdsbuf < efdsbuf) {
511 - fds = (struct kinfo_file *)(uintptr_t)bfdsbuf;
512 - if (unlikely(fds->kf_structsize == 0))
513 - break;
514 -
515 - // do not process file descriptors for current working directory, root directory,
516 - // jail directory, ktrace vnode, text vnode and controlling terminal
517 - if (unlikely(fds->kf_fd < 0)) {
518 - bfdsbuf += fds->kf_structsize;
519 - continue;
520 - }
521 -
522 - // get file descriptors array index
523 - size_t fdid = fds->kf_fd;
524 -
525 - // check if the fds array is small
526 - if (unlikely(fdid >= p->fds_size)) {
527 - // it is small, extend it
528 -
529 - debug_log("extending fd memory slots for %s from %d to %d", p->comm, p->fds_size, fdid + MAX_SPARE_FDS);
530 -
531 - p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(struct pid_fd));
532 -
533 - // and initialize it
534 - init_pid_fds(p, p->fds_size, (fdid + MAX_SPARE_FDS) - p->fds_size);
535 - p->fds_size = fdid + MAX_SPARE_FDS;
536 - }
537 -
538 - if (unlikely(p->fds[fdid].fd == 0)) {
539 - // we don't know this fd, get it
540 -
541 - switch (fds->kf_type) {
542 - case KF_TYPE_FIFO:
543 - case KF_TYPE_VNODE:
544 - if (unlikely(!fds->kf_path[0])) {
545 - sprintf(fdsname, "other: inode: %lu", fds->kf_un.kf_file.kf_file_fileid);
546 - break;
547 - }
548 - sprintf(fdsname, "%s", fds->kf_path);
549 - break;
550 - case KF_TYPE_SOCKET:
551 - switch (fds->kf_sock_domain) {
552 - case AF_INET:
553 - case AF_INET6:
554 -#if __FreeBSD_version < 1400074
555 - if (fds->kf_sock_protocol == IPPROTO_TCP)
556 - sprintf(fdsname, "socket: %d %lx", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sock_inpcb);
557 - else
558 -#endif
559 - sprintf(fdsname, "socket: %d %lx", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sock_pcb);
560 - break;
561 - case AF_UNIX:
562 - /* print address of pcb and connected pcb */
563 - sprintf(fdsname, "socket: %lx %lx", fds->kf_un.kf_sock.kf_sock_pcb, fds->kf_un.kf_sock.kf_sock_unpconn);
564 - break;
565 - default:
566 - /* print protocol number and socket address */
567 -#if __FreeBSD_version < 1200031
568 - sprintf(fdsname, "socket: other: %d %s %s", fds->kf_sock_protocol, fds->kf_sa_local.__ss_pad1, fds->kf_sa_local.__ss_pad2);
569 -#else
570 - sprintf(fdsname, "socket: other: %d %s %s", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sa_local.__ss_pad1, fds->kf_un.kf_sock.kf_sa_local.__ss_pad2);
571 -#endif
572 - }
573 - break;
574 - case KF_TYPE_PIPE:
575 - sprintf(fdsname, "pipe: %lu %lu", fds->kf_un.kf_pipe.kf_pipe_addr, fds->kf_un.kf_pipe.kf_pipe_peer);
576 - break;
577 - case KF_TYPE_PTS:
578 -#if __FreeBSD_version < 1200031
579 - sprintf(fdsname, "other: pts: %u", fds->kf_un.kf_pts.kf_pts_dev);
580 -#else
581 - sprintf(fdsname, "other: pts: %lu", fds->kf_un.kf_pts.kf_pts_dev);
582 -#endif
583 - break;
584 - case KF_TYPE_SHM:
585 - strncpyz(shm_name, fds->kf_path, FILENAME_MAX - SHM_FORMAT_LEN);
586 - sprintf(fdsname, "other: shm: %s size: %lu", shm_name, fds->kf_un.kf_file.kf_file_size);
587 - break;
588 - case KF_TYPE_SEM:
589 - sprintf(fdsname, "other: sem: %u", fds->kf_un.kf_sem.kf_sem_value);
590 - break;
591 - default:
592 - sprintf(fdsname, "other: pid: %d fd: %d", fds->kf_un.kf_proc.kf_pid, fds->kf_fd);
593 - }
594 -
595 - // if another process already has this, we will get
596 - // the same id
597 - p->fds[fdid].fd = file_descriptor_find_or_add(fdsname, 0);
598 - }
599 -
600 - // else make it positive again, we need it
601 - // of course, the actual file may have changed
602 -
603 - else
604 - p->fds[fdid].fd = -p->fds[fdid].fd;
605 -
606 - bfdsbuf += fds->kf_structsize;
607 - }
608 -
609 - return true;
610 -}
611 -#endif // __FreeBSD__
612 -
613 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
614 -static bool read_pid_file_descriptors_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
615 - if(unlikely(!p->fds_dirname)) {
616 - char dirname[FILENAME_MAX+1];
617 - snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", netdata_configured_host_prefix, p->pid);
618 - p->fds_dirname = strdupz(dirname);
619 - }
620 -
621 - DIR *fds = opendir(p->fds_dirname);
622 - if(unlikely(!fds)) return false;
623 -
624 - struct dirent *de;
625 - char linkname[FILENAME_MAX + 1];
626 -
627 - // we make all pid fds negative, so that
628 - // we can detect unused file descriptors
629 - // at the end, to free them
630 - make_all_pid_fds_negative(p);
631 -
632 - while((de = readdir(fds))) {
633 - // we need only files with numeric names
634 -
635 - if(unlikely(de->d_name[0] < '0' || de->d_name[0] > '9'))
636 - continue;
637 -
638 - // get its number
639 - int fdid = (int) str2l(de->d_name);
640 - if(unlikely(fdid < 0)) continue;
641 -
642 - // check if the fds array is small
643 - if(unlikely((size_t)fdid >= p->fds_size)) {
644 - // it is small, extend it
645 -
646 - debug_log("extending fd memory slots for %s from %d to %d"
647 - , p->comm
648 - , p->fds_size
649 - , fdid + MAX_SPARE_FDS
650 - );
651 -
652 - p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(struct pid_fd));
653 -
654 - // and initialize it
655 - init_pid_fds(p, p->fds_size, (fdid + MAX_SPARE_FDS) - p->fds_size);
656 - p->fds_size = (size_t)fdid + MAX_SPARE_FDS;
657 - }
658 -
659 - if(unlikely(p->fds[fdid].fd < 0 && de->d_ino != p->fds[fdid].inode)) {
660 - // inodes do not match, clear the previous entry
661 - inodes_changed_counter++;
662 - file_descriptor_not_used(-p->fds[fdid].fd);
663 - clear_pid_fd(&p->fds[fdid]);
664 - }
665 -
666 - if(p->fds[fdid].fd < 0 && p->fds[fdid].cache_iterations_counter > 0) {
667 - p->fds[fdid].fd = -p->fds[fdid].fd;
668 - p->fds[fdid].cache_iterations_counter--;
669 - continue;
670 - }
671 -
672 - if(unlikely(!p->fds[fdid].filename)) {
673 - filenames_allocated_counter++;
674 - char fdname[FILENAME_MAX + 1];
675 - snprintfz(fdname, FILENAME_MAX, "%s/proc/%d/fd/%s", netdata_configured_host_prefix, p->pid, de->d_name);
676 - p->fds[fdid].filename = strdupz(fdname);
677 - }
678 -
679 - file_counter++;
680 - ssize_t l = readlink(p->fds[fdid].filename, linkname, FILENAME_MAX);
681 - if(unlikely(l == -1)) {
682 - // cannot read the link
683 -
684 - if(debug_enabled || (p->target && p->target->debug_enabled))
685 - netdata_log_error("Cannot read link %s", p->fds[fdid].filename);
686 -
687 - if(unlikely(p->fds[fdid].fd < 0)) {
688 - file_descriptor_not_used(-p->fds[fdid].fd);
689 - clear_pid_fd(&p->fds[fdid]);
690 - }
691 -
692 - continue;
693 - }
694 - else
695 - linkname[l] = '\0';
696 -
697 - uint32_t link_hash = simple_hash(linkname);
698 -
699 - if(unlikely(p->fds[fdid].fd < 0 && p->fds[fdid].link_hash != link_hash)) {
700 - // the link changed
701 - links_changed_counter++;
702 - file_descriptor_not_used(-p->fds[fdid].fd);
703 - clear_pid_fd(&p->fds[fdid]);
704 - }
705 -
706 - if(unlikely(p->fds[fdid].fd == 0)) {
707 - // we don't know this fd, get it
708 -
709 - // if another process already has this, we will get
710 - // the same id
711 - p->fds[fdid].fd = file_descriptor_find_or_add(linkname, link_hash);
712 - p->fds[fdid].inode = de->d_ino;
713 - p->fds[fdid].link_hash = link_hash;
714 - }
715 - else {
716 - // else make it positive again, we need it
717 - p->fds[fdid].fd = -p->fds[fdid].fd;
718 - }
719 -
720 - // caching control
721 - // without this we read all the files on every iteration
722 - if(max_fds_cache_seconds > 0) {
723 - size_t spread = ((size_t)max_fds_cache_seconds > 10) ? 10 : (size_t)max_fds_cache_seconds;
724 -
725 - // cache it for a few iterations
726 - size_t max = ((size_t) max_fds_cache_seconds + (fdid % spread)) / (size_t) update_every;
727 - p->fds[fdid].cache_iterations_reset++;
728 -
729 - if(unlikely(p->fds[fdid].cache_iterations_reset % spread == (size_t) fdid % spread))
730 - p->fds[fdid].cache_iterations_reset++;
731 -
732 - if(unlikely((fdid <= 2 && p->fds[fdid].cache_iterations_reset > 5) ||
733 - p->fds[fdid].cache_iterations_reset > max)) {
734 - // for stdin, stdout, stderr (fdid <= 2) we have checked a few times, or if it goes above the max, goto max
735 - p->fds[fdid].cache_iterations_reset = max;
736 - }
737 -
738 - p->fds[fdid].cache_iterations_counter = p->fds[fdid].cache_iterations_reset;
739 - }
740 - }
741 -
742 - closedir(fds);
743 -
744 - return true;
745 -}
746 -#endif // !__FreeBSD__ !__APPLE
747 -
748 -int read_pid_file_descriptors(struct pid_stat *p, void *ptr) {
749 - bool ret = read_pid_file_descriptors_per_os(p, ptr);
750 - cleanup_negative_pid_fds(p);
751 -
752 - return ret ? 1 : 0;
753 -}
src/collectors/apps.plugin/apps_proc_pid_io.c deleted
-95
@@ -1,95 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "apps_plugin.h"
4 -
5 -static inline void clear_pid_io(struct pid_stat *p) {
6 - p->io_logical_bytes_read = 0;
7 - p->io_logical_bytes_written = 0;
8 - p->io_read_calls = 0;
9 - p->io_write_calls = 0;
10 - p->io_storage_bytes_read = 0;
11 - p->io_storage_bytes_written = 0;
12 - p->io_cancelled_write_bytes = 0;
13 -}
14 -
15 -#if defined(__FreeBSD__)
16 -static inline bool read_proc_pid_io_per_os(struct pid_stat *p, void *ptr) {
17 - struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr;
18 -
19 - pid_incremental_rate(io, p->io_storage_bytes_read, proc_info->ki_rusage.ru_inblock);
20 - pid_incremental_rate(io, p->io_storage_bytes_written, proc_info->ki_rusage.ru_oublock);
21 -
22 - p->io_logical_bytes_read = 0;
23 - p->io_logical_bytes_written = 0;
24 - p->io_read_calls = 0;
25 - p->io_write_calls = 0;
26 - p->io_cancelled_write_bytes = 0;
27 -
28 - return true;
29 -}
30 -#endif
31 -
32 -#ifdef __APPLE__
33 -static inline bool read_proc_pid_io_per_os(struct pid_stat *p, void *ptr) {
34 - struct pid_info *pi = ptr;
35 -
36 - // On MacOS, the proc_pid_rusage provides disk_io_statistics which includes io bytes read and written
37 - // but does not provide the same level of detail as Linux, like separating logical and physical I/O bytes.
38 - pid_incremental_rate(io, p->io_storage_bytes_read, pi->rusageinfo.ri_diskio_bytesread);
39 - pid_incremental_rate(io, p->io_storage_bytes_written, pi->rusageinfo.ri_diskio_byteswritten);
40 -
41 - p->io_logical_bytes_read = 0;
42 - p->io_logical_bytes_written = 0;
43 - p->io_read_calls = 0;
44 - p->io_write_calls = 0;
45 - p->io_cancelled_write_bytes = 0;
46 -
47 - return true;
48 -}
49 -#endif // __APPLE__
50 -
51 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
52 -static inline int read_proc_pid_io_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
53 - static procfile *ff = NULL;
54 -
55 - if(unlikely(!p->io_filename)) {
56 - char filename[FILENAME_MAX + 1];
57 - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", netdata_configured_host_prefix, p->pid);
58 - p->io_filename = strdupz(filename);
59 - }
60 -
61 - // open the file
62 - ff = procfile_reopen(ff, p->io_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
63 - if(unlikely(!ff)) goto cleanup;
64 -
65 - ff = procfile_readall(ff);
66 - if(unlikely(!ff)) goto cleanup;
67 -
68 - pid_incremental_rate(io, p->io_logical_bytes_read, str2kernel_uint_t(procfile_lineword(ff, 0, 1)));
69 - pid_incremental_rate(io, p->io_logical_bytes_written, str2kernel_uint_t(procfile_lineword(ff, 1, 1)));
70 - pid_incremental_rate(io, p->io_read_calls, str2kernel_uint_t(procfile_lineword(ff, 2, 1)));
71 - pid_incremental_rate(io, p->io_write_calls, str2kernel_uint_t(procfile_lineword(ff, 3, 1)));
72 - pid_incremental_rate(io, p->io_storage_bytes_read, str2kernel_uint_t(procfile_lineword(ff, 4, 1)));
73 - pid_incremental_rate(io, p->io_storage_bytes_written, str2kernel_uint_t(procfile_lineword(ff, 5, 1)));
74 - pid_incremental_rate(io, p->io_cancelled_write_bytes, str2kernel_uint_t(procfile_lineword(ff, 6, 1)));
75 -
76 - return true;
77 -
78 -cleanup:
79 - clear_pid_io(p);
80 - return false;
81 -}
82 -#endif // !__FreeBSD__ !__APPLE__
83 -
84 -int read_proc_pid_io(struct pid_stat *p, void *ptr) {
85 - p->last_io_collected_usec = p->io_collected_usec;
86 - p->io_collected_usec = now_monotonic_usec();
87 - calls_counter++;
88 -
89 - bool ret = read_proc_pid_io_per_os(p, ptr);
90 -
91 - if(unlikely(global_iterations_counter == 1))
92 - clear_pid_io(p);
93 -
94 - return ret ? 1 : 0;
95 -}
src/collectors/apps.plugin/apps_proc_pid_limits.c deleted
-151
@@ -1,151 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "apps_plugin.h"
4 -
5 -// ----------------------------------------------------------------------------
6 -
7 -#define MAX_PROC_PID_LIMITS 8192
8 -#define PROC_PID_LIMITS_MAX_OPEN_FILES_KEY "\nMax open files "
9 -
10 -static inline kernel_uint_t get_proc_pid_limits_limit(char *buf, const char *key, size_t key_len, kernel_uint_t def) {
11 - char *line = strstr(buf, key);
12 - if(!line)
13 - return def;
14 -
15 - char *v = &line[key_len];
16 - while(isspace(*v)) v++;
17 -
18 - if(strcmp(v, "unlimited") == 0)
19 - return 0;
20 -
21 - return str2ull(v, NULL);
22 -}
23 -
24 -#if defined(__FreeBSD__) || defined(__APPLE__)
25 -int read_proc_pid_limits_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
26 - return false;
27 -}
28 -#endif
29 -
30 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
31 -static inline bool read_proc_pid_limits_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
32 - static char proc_pid_limits_buffer[MAX_PROC_PID_LIMITS + 1];
33 - bool ret = false;
34 - bool read_limits = false;
35 -
36 - errno_clear();
37 - proc_pid_limits_buffer[0] = '\0';
38 -
39 - kernel_uint_t all_fds = pid_openfds_sum(p);
40 - if(all_fds < p->limits.max_open_files / 2 && p->io_collected_usec > p->last_limits_collected_usec && p->io_collected_usec - p->last_limits_collected_usec <= 60 * USEC_PER_SEC) {
41 - // too frequent, we want to collect limits once per minute
42 - ret = true;
43 - goto cleanup;
44 - }
45 -
46 - if(unlikely(!p->limits_filename)) {
47 - char filename[FILENAME_MAX + 1];
48 - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/limits", netdata_configured_host_prefix, p->pid);
49 - p->limits_filename = strdupz(filename);
50 - }
51 -
52 - int fd = open(p->limits_filename, procfile_open_flags, 0666);
53 - if(unlikely(fd == -1)) goto cleanup;
54 -
55 - ssize_t bytes = read(fd, proc_pid_limits_buffer, MAX_PROC_PID_LIMITS);
56 - close(fd);
57 -
58 - if(bytes <= 0)
59 - goto cleanup;
60 -
61 - // make it '\0' terminated
62 - if(bytes < MAX_PROC_PID_LIMITS)
63 - proc_pid_limits_buffer[bytes] = '\0';
64 - else
65 - proc_pid_limits_buffer[MAX_PROC_PID_LIMITS - 1] = '\0';
66 -
67 - p->limits.max_open_files = get_proc_pid_limits_limit(proc_pid_limits_buffer, PROC_PID_LIMITS_MAX_OPEN_FILES_KEY, sizeof(PROC_PID_LIMITS_MAX_OPEN_FILES_KEY) - 1, 0);
68 - if(p->limits.max_open_files == 1) {
69 - // it seems a bug in the kernel or something similar
70 - // it sets max open files to 1 but the number of files
71 - // the process has open are more than 1...
72 - // https://github.com/netdata/netdata/issues/15443
73 - p->limits.max_open_files = 0;
74 - ret = true;
75 - goto cleanup;
76 - }
77 -
78 - p->last_limits_collected_usec = p->io_collected_usec;
79 - read_limits = true;
80 -
81 - ret = true;
82 -
83 -cleanup:
84 - if(p->limits.max_open_files)
85 - p->openfds_limits_percent = (NETDATA_DOUBLE)all_fds * 100.0 / (NETDATA_DOUBLE)p->limits.max_open_files;
86 - else
87 - p->openfds_limits_percent = 0.0;
88 -
89 - if(p->openfds_limits_percent > 100.0) {
90 - if(!(p->log_thrown & PID_LOG_LIMITS_DETAIL)) {
91 - char *line;
92 -
93 - if(!read_limits) {
94 - proc_pid_limits_buffer[0] = '\0';
95 - line = "NOT READ";
96 - }
97 - else {
98 - line = strstr(proc_pid_limits_buffer, PROC_PID_LIMITS_MAX_OPEN_FILES_KEY);
99 - if (line) {
100 - line++; // skip the initial newline
101 -
102 - char *end = strchr(line, '\n');
103 - if (end)
104 - *end = '\0';
105 - }
106 - }
107 -
108 - netdata_log_info(
109 - "FDS_LIMITS: PID %d (%s) is using "
110 - "%0.2f %% of its fds limits, "
111 - "open fds = %"PRIu64 "("
112 - "files = %"PRIu64 ", "
113 - "pipes = %"PRIu64 ", "
114 - "sockets = %"PRIu64", "
115 - "inotifies = %"PRIu64", "
116 - "eventfds = %"PRIu64", "
117 - "timerfds = %"PRIu64", "
118 - "signalfds = %"PRIu64", "
119 - "eventpolls = %"PRIu64" "
120 - "other = %"PRIu64" "
121 - "), open fds limit = %"PRIu64", "
122 - "%s, "
123 - "original line [%s]",
124 - p->pid, p->comm, p->openfds_limits_percent, all_fds,
125 - p->openfds.files,
126 - p->openfds.pipes,
127 - p->openfds.sockets,
128 - p->openfds.inotifies,
129 - p->openfds.eventfds,
130 - p->openfds.timerfds,
131 - p->openfds.signalfds,
132 - p->openfds.eventpolls,
133 - p->openfds.other,
134 - p->limits.max_open_files,
135 - read_limits ? "and we have read the limits AFTER counting the fds"
136 - : "but we have read the limits BEFORE counting the fds",
137 - line);
138 -
139 - p->log_thrown |= PID_LOG_LIMITS_DETAIL;
140 - }
141 - }
142 - else
143 - p->log_thrown &= ~PID_LOG_LIMITS_DETAIL;
144 -
145 - return ret;
146 -}
147 -#endif // !__FreeBSD__ !__APPLE__
148 -
149 -int read_proc_pid_limits(struct pid_stat *p, void *ptr) {
150 - return read_proc_pid_limits_per_os(p, ptr) ? 1 : 0;
151 -}
src/collectors/apps.plugin/apps_proc_pid_stat.c deleted
-293
@@ -1,293 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "apps_plugin.h"
4 -
5 -// ----------------------------------------------------------------------------
6 -
7 -static inline void assign_target_to_pid(struct pid_stat *p) {
8 - targets_assignment_counter++;
9 -
10 - uint32_t hash = simple_hash(p->comm);
11 - size_t pclen = strlen(p->comm);
12 -
13 - struct target *w;
14 - for(w = apps_groups_root_target; w ; w = w->next) {
15 - // if(debug_enabled || (p->target && p->target->debug_enabled)) debug_log_int("\t\tcomparing '%s' with '%s'", w->compare, p->comm);
16 -
17 - // find it - 4 cases:
18 - // 1. the target is not a pattern
19 - // 2. the target has the prefix
20 - // 3. the target has the suffix
21 - // 4. the target is something inside cmdline
22 -
23 - if(unlikely(( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
24 - || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
25 - || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
26 - || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && p->cmdline && strstr(p->cmdline, w->compare))
27 - ))) {
28 -
29 - p->matched_by_config = true;
30 - if(w->target) p->target = w->target;
31 - else p->target = w;
32 -
33 - if(debug_enabled || (p->target && p->target->debug_enabled))
34 - debug_log_int("%s linked to target %s", p->comm, p->target->name);
35 -
36 - break;
37 - }
38 - }
39 -}
40 -
41 -static inline void update_pid_comm(struct pid_stat *p, const char *comm) {
42 - if(strcmp(p->comm, comm) != 0) {
43 - if(unlikely(debug_enabled)) {
44 - if(p->comm[0])
45 - debug_log("\tpid %d (%s) changed name to '%s'", p->pid, p->comm, comm);
46 - else
47 - debug_log("\tJust added %d (%s)", p->pid, comm);
48 - }
49 -
50 - strncpyz(p->comm, comm, MAX_COMPARE_NAME);
51 -
52 - // /proc/<pid>/cmdline
53 - if(likely(proc_pid_cmdline_is_needed))
54 - managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
55 -
56 - assign_target_to_pid(p);
57 - }
58 -}
59 -
60 -static inline void clear_pid_stat(struct pid_stat *p, bool threads) {
61 - p->minflt = 0;
62 - p->cminflt = 0;
63 - p->majflt = 0;
64 - p->cmajflt = 0;
65 - p->utime = 0;
66 - p->stime = 0;
67 - p->gtime = 0;
68 - p->cutime = 0;
69 - p->cstime = 0;
70 - p->cgtime = 0;
71 -
72 - if(threads)
73 - p->num_threads = 0;
74 -
75 - // p->rss = 0;
76 -}
77 -
78 -#if defined(__FreeBSD__)
79 -static inline bool read_proc_pid_stat_per_os(struct pid_stat *p, void *ptr) {
80 - struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr;
81 - if (unlikely(proc_info->ki_tdflags & TDF_IDLETD))
82 - goto cleanup;
83 -
84 - char *comm = proc_info->ki_comm;
85 - p->ppid = proc_info->ki_ppid;
86 -
87 - update_pid_comm(p, comm);
88 -
89 - pid_incremental_rate(stat, p->minflt, (kernel_uint_t)proc_info->ki_rusage.ru_minflt);
90 - pid_incremental_rate(stat, p->cminflt, (kernel_uint_t)proc_info->ki_rusage_ch.ru_minflt);
91 - pid_incremental_rate(stat, p->majflt, (kernel_uint_t)proc_info->ki_rusage.ru_majflt);
92 - pid_incremental_rate(stat, p->cmajflt, (kernel_uint_t)proc_info->ki_rusage_ch.ru_majflt);
93 - pid_incremental_rate(stat, p->utime, (kernel_uint_t)proc_info->ki_rusage.ru_utime.tv_sec * 100 + proc_info->ki_rusage.ru_utime.tv_usec / 10000);
94 - pid_incremental_rate(stat, p->stime, (kernel_uint_t)proc_info->ki_rusage.ru_stime.tv_sec * 100 + proc_info->ki_rusage.ru_stime.tv_usec / 10000);
95 - pid_incremental_rate(stat, p->cutime, (kernel_uint_t)proc_info->ki_rusage_ch.ru_utime.tv_sec * 100 + proc_info->ki_rusage_ch.ru_utime.tv_usec / 10000);
96 - pid_incremental_rate(stat, p->cstime, (kernel_uint_t)proc_info->ki_rusage_ch.ru_stime.tv_sec * 100 + proc_info->ki_rusage_ch.ru_stime.tv_usec / 10000);
97 -
98 - p->num_threads = proc_info->ki_numthreads;
99 -
100 - usec_t started_ut = timeval_usec(&proc_info->ki_start);
101 - p->uptime = (system_current_time_ut > started_ut) ? (system_current_time_ut - started_ut) / USEC_PER_SEC : 0;
102 -
103 - if(enable_guest_charts) {
104 - enable_guest_charts = false;
105 - netdata_log_info("Guest charts aren't supported by FreeBSD");
106 - }
107 -
108 - if(unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
109 - debug_log_int("READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT ", threads=%d", netdata_configured_host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
110 -
111 - if(unlikely(global_iterations_counter == 1))
112 - clear_pid_stat(p, false);
113 -
114 - return true;
115 -
116 -cleanup:
117 - clear_pid_stat(p, true);
118 - return false;
119 -}
120 -#endif // __FreeBSD__
121 -
122 -#ifdef __APPLE__
123 -static inline bool read_proc_pid_stat_per_os(struct pid_stat *p, void *ptr) {
124 - struct pid_info *pi = ptr;
125 -
126 - p->ppid = pi->proc.kp_eproc.e_ppid;
127 -
128 - // Update command name and target if changed
129 - char comm[PROC_PIDPATHINFO_MAXSIZE];
130 - int ret = proc_name(p->pid, comm, sizeof(comm));
131 - if (ret <= 0)
132 - strncpyz(comm, "unknown", sizeof(comm) - 1);
133 -
134 - update_pid_comm(p, comm);
135 -
136 - kernel_uint_t userCPU = (pi->taskinfo.pti_total_user * mach_info.numer) / mach_info.denom / NSEC_PER_USEC / 10000;
137 - kernel_uint_t systemCPU = (pi->taskinfo.pti_total_system * mach_info.numer) / mach_info.denom / NSEC_PER_USEC / 10000;
138 -
139 - // Map the values from taskinfo to the pid_stat structure
140 - pid_incremental_rate(stat, p->minflt, pi->taskinfo.pti_faults);
141 - pid_incremental_rate(stat, p->majflt, pi->taskinfo.pti_pageins);
142 - pid_incremental_rate(stat, p->utime, userCPU);
143 - pid_incremental_rate(stat, p->stime, systemCPU);
144 - p->num_threads = pi->taskinfo.pti_threadnum;
145 -
146 - usec_t started_ut = timeval_usec(&pi->proc.kp_proc.p_starttime);
147 - p->uptime = (system_current_time_ut > started_ut) ? (system_current_time_ut - started_ut) / USEC_PER_SEC : 0;
148 -
149 - // Note: Some values such as guest time, cutime, cstime, etc., are not directly available in MacOS.
150 - // You might need to approximate or leave them unset depending on your needs.
151 -
152 - if(unlikely(debug_enabled || (p->target && p->target->debug_enabled))) {
153 - debug_log_int("READ PROC/PID/STAT for MacOS: process: '%s' on target '%s' VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", threads=%d",
154 - p->comm, (p->target) ? p->target->name : "UNSET", p->utime, p->stime, p->minflt, p->majflt, p->num_threads);
155 - }
156 -
157 - if(unlikely(global_iterations_counter == 1))
158 - clear_pid_stat(p, false);
159 -
160 - // MacOS doesn't have a direct concept of process state like Linux,
161 - // so updating process state count might need a different approach.
162 -
163 - return true;
164 -}
165 -#endif // __APPLE__
166 -
167 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
168 -static inline void update_proc_state_count(char proc_stt) {
169 - switch (proc_stt) {
170 - case 'S':
171 - proc_state_count[PROC_STATUS_SLEEPING] += 1;
172 - break;
173 - case 'R':
174 - proc_state_count[PROC_STATUS_RUNNING] += 1;
175 - break;
176 - case 'D':
177 - proc_state_count[PROC_STATUS_SLEEPING_D] += 1;
178 - break;
179 - case 'Z':
180 - proc_state_count[PROC_STATUS_ZOMBIE] += 1;
181 - break;
182 - case 'T':
183 - proc_state_count[PROC_STATUS_STOPPED] += 1;
184 - break;
185 - default:
186 - break;
187 - }
188 -}
189 -
190 -static inline bool read_proc_pid_stat_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
191 - static procfile *ff = NULL;
192 -
193 - if(unlikely(!p->stat_filename)) {
194 - char filename[FILENAME_MAX + 1];
195 - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", netdata_configured_host_prefix, p->pid);
196 - p->stat_filename = strdupz(filename);
197 - }
198 -
199 - int set_quotes = (!ff)?1:0;
200 -
201 - ff = procfile_reopen(ff, p->stat_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
202 - if(unlikely(!ff)) goto cleanup;
203 -
204 - // if(set_quotes) procfile_set_quotes(ff, "()");
205 - if(unlikely(set_quotes))
206 - procfile_set_open_close(ff, "(", ")");
207 -
208 - ff = procfile_readall(ff);
209 - if(unlikely(!ff)) goto cleanup;
210 -
211 - // p->pid = str2pid_t(procfile_lineword(ff, 0, 0));
212 - char *comm = procfile_lineword(ff, 0, 1);
213 - p->state = *(procfile_lineword(ff, 0, 2));
214 - p->ppid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 3));
215 - // p->pgrp = (int32_t)str2pid_t(procfile_lineword(ff, 0, 4));
216 - // p->session = (int32_t)str2pid_t(procfile_lineword(ff, 0, 5));
217 - // p->tty_nr = (int32_t)str2pid_t(procfile_lineword(ff, 0, 6));
218 - // p->tpgid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 7));
219 - // p->flags = str2uint64_t(procfile_lineword(ff, 0, 8));
220 -
221 - update_pid_comm(p, comm);
222 -
223 - pid_incremental_rate(stat, p->minflt, str2kernel_uint_t(procfile_lineword(ff, 0, 9)));
224 - pid_incremental_rate(stat, p->cminflt, str2kernel_uint_t(procfile_lineword(ff, 0, 10)));
225 - pid_incremental_rate(stat, p->majflt, str2kernel_uint_t(procfile_lineword(ff, 0, 11)));
226 - pid_incremental_rate(stat, p->cmajflt, str2kernel_uint_t(procfile_lineword(ff, 0, 12)));
227 - pid_incremental_rate(stat, p->utime, str2kernel_uint_t(procfile_lineword(ff, 0, 13)));
228 - pid_incremental_rate(stat, p->stime, str2kernel_uint_t(procfile_lineword(ff, 0, 14)));
229 - pid_incremental_rate(stat, p->cutime, str2kernel_uint_t(procfile_lineword(ff, 0, 15)));
230 - pid_incremental_rate(stat, p->cstime, str2kernel_uint_t(procfile_lineword(ff, 0, 16)));
231 - // p->priority = str2kernel_uint_t(procfile_lineword(ff, 0, 17));
232 - // p->nice = str2kernel_uint_t(procfile_lineword(ff, 0, 18));
233 - p->num_threads = (int32_t) str2uint32_t(procfile_lineword(ff, 0, 19), NULL);
234 - // p->itrealvalue = str2kernel_uint_t(procfile_lineword(ff, 0, 20));
235 - kernel_uint_t collected_starttime = str2kernel_uint_t(procfile_lineword(ff, 0, 21)) / system_hz;
236 - p->uptime = (system_uptime_secs > collected_starttime)?(system_uptime_secs - collected_starttime):0;
237 - // p->vsize = str2kernel_uint_t(procfile_lineword(ff, 0, 22));
238 - // p->rss = str2kernel_uint_t(procfile_lineword(ff, 0, 23));
239 - // p->rsslim = str2kernel_uint_t(procfile_lineword(ff, 0, 24));
240 - // p->starcode = str2kernel_uint_t(procfile_lineword(ff, 0, 25));
241 - // p->endcode = str2kernel_uint_t(procfile_lineword(ff, 0, 26));
242 - // p->startstack = str2kernel_uint_t(procfile_lineword(ff, 0, 27));
243 - // p->kstkesp = str2kernel_uint_t(procfile_lineword(ff, 0, 28));
244 - // p->kstkeip = str2kernel_uint_t(procfile_lineword(ff, 0, 29));
245 - // p->signal = str2kernel_uint_t(procfile_lineword(ff, 0, 30));
246 - // p->blocked = str2kernel_uint_t(procfile_lineword(ff, 0, 31));
247 - // p->sigignore = str2kernel_uint_t(procfile_lineword(ff, 0, 32));
248 - // p->sigcatch = str2kernel_uint_t(procfile_lineword(ff, 0, 33));
249 - // p->wchan = str2kernel_uint_t(procfile_lineword(ff, 0, 34));
250 - // p->nswap = str2kernel_uint_t(procfile_lineword(ff, 0, 35));
251 - // p->cnswap = str2kernel_uint_t(procfile_lineword(ff, 0, 36));
252 - // p->exit_signal = str2kernel_uint_t(procfile_lineword(ff, 0, 37));
253 - // p->processor = str2kernel_uint_t(procfile_lineword(ff, 0, 38));
254 - // p->rt_priority = str2kernel_uint_t(procfile_lineword(ff, 0, 39));
255 - // p->policy = str2kernel_uint_t(procfile_lineword(ff, 0, 40));
256 - // p->delayacct_blkio_ticks = str2kernel_uint_t(procfile_lineword(ff, 0, 41));
257 -
258 - if(enable_guest_charts) {
259 - pid_incremental_rate(stat, p->gtime, str2kernel_uint_t(procfile_lineword(ff, 0, 42)));
260 - pid_incremental_rate(stat, p->cgtime, str2kernel_uint_t(procfile_lineword(ff, 0, 43)));
261 -
262 - if (show_guest_time || p->gtime || p->cgtime) {
263 - p->utime -= (p->utime >= p->gtime) ? p->gtime : p->utime;
264 - p->cutime -= (p->cutime >= p->cgtime) ? p->cgtime : p->cutime;
265 - show_guest_time = 1;
266 - }
267 - }
268 -
269 - if(unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
270 - debug_log_int("READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT ", threads=%d", netdata_configured_host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
271 -
272 - if(unlikely(global_iterations_counter == 1))
273 - clear_pid_stat(p, false);
274 -
275 - update_proc_state_count(p->state);
276 - return true;
277 -
278 -cleanup:
279 - clear_pid_stat(p, true);
280 - return false;
281 -}
282 -#endif // !__FreeBSD__ !__APPLE__
283 -
284 -int read_proc_pid_stat(struct pid_stat *p, void *ptr) {
285 - p->last_stat_collected_usec = p->stat_collected_usec;
286 - p->stat_collected_usec = now_monotonic_usec();
287 - calls_counter++;
288 -
289 - if(!read_proc_pid_stat_per_os(p, ptr))
290 - return 0;
291 -
292 - return 1;
293 -}
src/collectors/apps.plugin/apps_proc_pid_status.c deleted
-192
@@ -1,192 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "apps_plugin.h"
4 -
5 -#if defined(__FreeBSD__)
6 -static inline bool read_proc_pid_status_per_os(struct pid_stat *p, void *ptr) {
7 - struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr;
8 -
9 - p->uid = proc_info->ki_uid;
10 - p->gid = proc_info->ki_groups[0];
11 - p->status_vmsize = proc_info->ki_size / 1024; // in KiB
12 - p->status_vmrss = proc_info->ki_rssize * pagesize / 1024; // in KiB
13 - // TODO: what about shared and swap memory on FreeBSD?
14 - return true;
15 -}
16 -#endif
17 -
18 -#ifdef __APPLE__
19 -static inline bool read_proc_pid_status_per_os(struct pid_stat *p, void *ptr) {
20 - struct pid_info *pi = ptr;
21 -
22 - p->uid = pi->bsdinfo.pbi_uid;
23 - p->gid = pi->bsdinfo.pbi_gid;
24 - p->status_vmsize = pi->taskinfo.pti_virtual_size / 1024; // Convert bytes to KiB
25 - p->status_vmrss = pi->taskinfo.pti_resident_size / 1024; // Convert bytes to KiB
26 - // p->status_vmswap = rusageinfo.ri_swapins + rusageinfo.ri_swapouts; // This is not directly available, consider an alternative representation
27 - p->status_voluntary_ctxt_switches = pi->taskinfo.pti_csw;
28 - // p->status_nonvoluntary_ctxt_switches = taskinfo.pti_nivcsw;
29 -
30 - return true;
31 -}
32 -#endif // __APPLE__
33 -
34 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
35 -struct arl_callback_ptr {
36 - struct pid_stat *p;
37 - procfile *ff;
38 - size_t line;
39 -};
40 -
41 -void arl_callback_status_uid(const char *name, uint32_t hash, const char *value, void *dst) {
42 - (void)name; (void)hash; (void)value;
43 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
44 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 5)) return;
45 -
46 - //const char *real_uid = procfile_lineword(aptr->ff, aptr->line, 1);
47 - const char *effective_uid = procfile_lineword(aptr->ff, aptr->line, 2);
48 - //const char *saved_uid = procfile_lineword(aptr->ff, aptr->line, 3);
49 - //const char *filesystem_uid = procfile_lineword(aptr->ff, aptr->line, 4);
50 -
51 - if(likely(effective_uid && *effective_uid))
52 - aptr->p->uid = (uid_t)str2l(effective_uid);
53 -}
54 -
55 -void arl_callback_status_gid(const char *name, uint32_t hash, const char *value, void *dst) {
56 - (void)name; (void)hash; (void)value;
57 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
58 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 5)) return;
59 -
60 - //const char *real_gid = procfile_lineword(aptr->ff, aptr->line, 1);
61 - const char *effective_gid = procfile_lineword(aptr->ff, aptr->line, 2);
62 - //const char *saved_gid = procfile_lineword(aptr->ff, aptr->line, 3);
63 - //const char *filesystem_gid = procfile_lineword(aptr->ff, aptr->line, 4);
64 -
65 - if(likely(effective_gid && *effective_gid))
66 - aptr->p->gid = (uid_t)str2l(effective_gid);
67 -}
68 -
69 -void arl_callback_status_vmsize(const char *name, uint32_t hash, const char *value, void *dst) {
70 - (void)name; (void)hash; (void)value;
71 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
72 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
73 -
74 - aptr->p->status_vmsize = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
75 -}
76 -
77 -void arl_callback_status_vmswap(const char *name, uint32_t hash, const char *value, void *dst) {
78 - (void)name; (void)hash; (void)value;
79 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
80 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
81 -
82 - aptr->p->status_vmswap = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
83 -}
84 -
85 -void arl_callback_status_vmrss(const char *name, uint32_t hash, const char *value, void *dst) {
86 - (void)name; (void)hash; (void)value;
87 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
88 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
89 -
90 - aptr->p->status_vmrss = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
91 -}
92 -
93 -void arl_callback_status_rssfile(const char *name, uint32_t hash, const char *value, void *dst) {
94 - (void)name; (void)hash; (void)value;
95 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
96 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
97 -
98 - aptr->p->status_rssfile = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
99 -}
100 -
101 -void arl_callback_status_rssshmem(const char *name, uint32_t hash, const char *value, void *dst) {
102 - (void)name; (void)hash; (void)value;
103 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
104 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
105 -
106 - aptr->p->status_rssshmem = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
107 -}
108 -
109 -void arl_callback_status_voluntary_ctxt_switches(const char *name, uint32_t hash, const char *value, void *dst) {
110 - (void)name; (void)hash; (void)value;
111 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
112 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 2)) return;
113 -
114 - struct pid_stat *p = aptr->p;
115 - pid_incremental_rate(stat, p->status_voluntary_ctxt_switches, str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1)));
116 -}
117 -
118 -void arl_callback_status_nonvoluntary_ctxt_switches(const char *name, uint32_t hash, const char *value, void *dst) {
119 - (void)name; (void)hash; (void)value;
120 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
121 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 2)) return;
122 -
123 - struct pid_stat *p = aptr->p;
124 - pid_incremental_rate(stat, p->status_nonvoluntary_ctxt_switches, str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1)));
125 -}
126 -
127 -static inline bool read_proc_pid_status_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
128 - static struct arl_callback_ptr arl_ptr;
129 - static procfile *ff = NULL;
130 -
131 - if(unlikely(!p->status_arl)) {
132 - p->status_arl = arl_create("/proc/pid/status", NULL, 60);
133 - arl_expect_custom(p->status_arl, "Uid", arl_callback_status_uid, &arl_ptr);
134 - arl_expect_custom(p->status_arl, "Gid", arl_callback_status_gid, &arl_ptr);
135 - arl_expect_custom(p->status_arl, "VmSize", arl_callback_status_vmsize, &arl_ptr);
136 - arl_expect_custom(p->status_arl, "VmRSS", arl_callback_status_vmrss, &arl_ptr);
137 - arl_expect_custom(p->status_arl, "RssFile", arl_callback_status_rssfile, &arl_ptr);
138 - arl_expect_custom(p->status_arl, "RssShmem", arl_callback_status_rssshmem, &arl_ptr);
139 - arl_expect_custom(p->status_arl, "VmSwap", arl_callback_status_vmswap, &arl_ptr);
140 - arl_expect_custom(p->status_arl, "voluntary_ctxt_switches", arl_callback_status_voluntary_ctxt_switches, &arl_ptr);
141 - arl_expect_custom(p->status_arl, "nonvoluntary_ctxt_switches", arl_callback_status_nonvoluntary_ctxt_switches, &arl_ptr);
142 - }
143 -
144 - if(unlikely(!p->status_filename)) {
145 - char filename[FILENAME_MAX + 1];
146 - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/status", netdata_configured_host_prefix, p->pid);
147 - p->status_filename = strdupz(filename);
148 - }
149 -
150 - ff = procfile_reopen(ff, p->status_filename, (!ff)?" \t:,-()/":NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
151 - if(unlikely(!ff)) return false;
152 -
153 - ff = procfile_readall(ff);
154 - if(unlikely(!ff)) return false;
155 -
156 - calls_counter++;
157 -
158 - // let ARL use this pid
159 - arl_ptr.p = p;
160 - arl_ptr.ff = ff;
161 -
162 - size_t lines = procfile_lines(ff), l;
163 - arl_begin(p->status_arl);
164 -
165 - for(l = 0; l < lines ;l++) {
166 - // debug_log("CHECK: line %zu of %zu, key '%s' = '%s'", l, lines, procfile_lineword(ff, l, 0), procfile_lineword(ff, l, 1));
167 - arl_ptr.line = l;
168 - if(unlikely(arl_check(p->status_arl,
169 - procfile_lineword(ff, l, 0),
170 - procfile_lineword(ff, l, 1)))) break;
171 - }
172 -
173 - p->status_vmshared = p->status_rssfile + p->status_rssshmem;
174 -
175 - // debug_log("%s uid %d, gid %d, VmSize %zu, VmRSS %zu, RssFile %zu, RssShmem %zu, shared %zu", p->comm, (int)p->uid, (int)p->gid, p->status_vmsize, p->status_vmrss, p->status_rssfile, p->status_rssshmem, p->status_vmshared);
176 -
177 - return true;
178 -}
179 -#endif // !__FreeBSD__ !__APPLE__
180 -
181 -int read_proc_pid_status(struct pid_stat *p, void *ptr) {
182 - p->status_vmsize = 0;
183 - p->status_vmrss = 0;
184 - p->status_vmshared = 0;
185 - p->status_rssfile = 0;
186 - p->status_rssshmem = 0;
187 - p->status_vmswap = 0;
188 - p->status_voluntary_ctxt_switches = 0;
189 - p->status_nonvoluntary_ctxt_switches = 0;
190 -
191 - return read_proc_pid_status_per_os(p, ptr) ? 1 : 0;
192 -}
src/collectors/apps.plugin/apps_proc_pids.c deleted
-720
@@ -1,720 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "apps_plugin.h"
4 -
5 -static struct pid_stat **all_pids = NULL;
6 -size_t all_pids_count = 0; // the number of processes running
7 -
8 -struct pid_stat *root_of_pids = NULL; // global linked list of all processes running
9 -
10 -#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
11 -// Another pre-allocated list of all possible pids.
12 -// We need it to assign them a unique sortlist id, so that we
13 -// read parents before children. This is needed to prevent a situation where
14 -// a child is found running, but until we read its parent, it has exited and
15 -// its parent has accumulated its resources.
16 -pid_t *all_pids_sortlist = NULL;
17 -#endif
18 -
19 -void pids_init(void) {
20 -#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
21 - all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max + 1);
22 -#endif
23 -
24 - all_pids = callocz(sizeof(struct pid_stat *), (size_t) pid_max + 1);
25 -}
26 -
27 -inline struct pid_stat *find_pid_entry(pid_t pid) {
28 - return all_pids[pid];
29 -}
30 -
31 -static inline struct pid_stat *get_or_allocate_pid_entry(pid_t pid) {
32 - struct pid_stat *p = find_pid_entry(pid);
33 - if(likely(p))
34 - return p;
35 -
36 - p = callocz(sizeof(struct pid_stat), 1);
37 - p->fds = mallocz(sizeof(struct pid_fd) * MAX_SPARE_FDS);
38 - p->fds_size = MAX_SPARE_FDS;
39 - init_pid_fds(p, 0, p->fds_size);
40 - p->pid = pid;
41 -
42 - DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(root_of_pids, p, prev, next);
43 - all_pids[pid] = p;
44 - all_pids_count++;
45 -
46 - return p;
47 -}
48 -
49 -static inline void del_pid_entry(pid_t pid) {
50 - struct pid_stat *p = find_pid_entry(pid);
51 -
52 - if(unlikely(!p)) {
53 - netdata_log_error("attempted to free pid %d that is not allocated.", pid);
54 - return;
55 - }
56 -
57 - debug_log("process %d %s exited, deleting it.", pid, p->comm);
58 -
59 - DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(root_of_pids, p, prev, next);
60 -
61 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
62 - {
63 - size_t i;
64 - for(i = 0; i < p->fds_size; i++)
65 - if(p->fds[i].filename)
66 - freez(p->fds[i].filename);
67 - }
68 - arl_free(p->status_arl);
69 -#endif
70 -
71 - freez(p->fds);
72 - freez(p->fds_dirname);
73 - freez(p->stat_filename);
74 - freez(p->status_filename);
75 - freez(p->limits_filename);
76 - freez(p->io_filename);
77 - freez(p->cmdline_filename);
78 - freez(p->cmdline);
79 - freez(p);
80 -
81 - all_pids[pid] = NULL;
82 - all_pids_count--;
83 -}
84 -
85 -static inline int collect_data_for_pid(pid_t pid, void *ptr) {
86 - if(unlikely(pid < 0 || pid > pid_max)) {
87 - netdata_log_error("Invalid pid %d read (expected %d to %d). Ignoring process.", pid, 0, pid_max);
88 - return 0;
89 - }
90 -
91 - struct pid_stat *p = get_or_allocate_pid_entry(pid);
92 - if(unlikely(!p || p->read)) return 0;
93 - p->read = true;
94 -
95 - // debug_log("Reading process %d (%s), sortlist %d", p->pid, p->comm, p->sortlist);
96 -
97 - // --------------------------------------------------------------------
98 - // /proc/<pid>/stat
99 -
100 - if(unlikely(!managed_log(p, PID_LOG_STAT, read_proc_pid_stat(p, ptr))))
101 - // there is no reason to proceed if we cannot get its status
102 - return 0;
103 -
104 - // check its parent pid
105 - if(unlikely(p->ppid < 0 || p->ppid > pid_max)) {
106 - netdata_log_error("Pid %d (command '%s') states invalid parent pid %d. Using 0.", pid, p->comm, p->ppid);
107 - p->ppid = 0;
108 - }
109 -
110 - // --------------------------------------------------------------------
111 - // /proc/<pid>/io
112 -
113 - managed_log(p, PID_LOG_IO, read_proc_pid_io(p, ptr));
114 -
115 - // --------------------------------------------------------------------
116 - // /proc/<pid>/status
117 -
118 - if(unlikely(!managed_log(p, PID_LOG_STATUS, read_proc_pid_status(p, ptr))))
119 - // there is no reason to proceed if we cannot get its status
120 - return 0;
121 -
122 - // --------------------------------------------------------------------
123 - // /proc/<pid>/fd
124 -
125 - if(enable_file_charts) {
126 - managed_log(p, PID_LOG_FDS, read_pid_file_descriptors(p, ptr));
127 - managed_log(p, PID_LOG_LIMITS, read_proc_pid_limits(p, ptr));
128 - }
129 -
130 - // --------------------------------------------------------------------
131 - // done!
132 -
133 - if(unlikely(debug_enabled && include_exited_childs && all_pids_count && p->ppid && all_pids[p->ppid] && !all_pids[p->ppid]->read))
134 - debug_log("Read process %d (%s) sortlisted %d, but its parent %d (%s) sortlisted %d, is not read", p->pid, p->comm, p->sortlist, all_pids[p->ppid]->pid, all_pids[p->ppid]->comm, all_pids[p->ppid]->sortlist);
135 -
136 - // mark it as updated
137 - p->updated = true;
138 - p->keep = false;
139 - p->keeploops = 0;
140 -
141 - return 1;
142 -}
143 -
144 -void cleanup_exited_pids(void) {
145 - size_t c;
146 - struct pid_stat *p = NULL;
147 -
148 - for(p = root_of_pids; p ;) {
149 - if(!p->updated && (!p->keep || p->keeploops > 0)) {
150 - if(unlikely(debug_enabled && (p->keep || p->keeploops)))
151 - debug_log(" > CLEANUP cannot keep exited process %d (%s) anymore - removing it.", p->pid, p->comm);
152 -
153 - for(c = 0; c < p->fds_size; c++)
154 - if(p->fds[c].fd > 0) {
155 - file_descriptor_not_used(p->fds[c].fd);
156 - clear_pid_fd(&p->fds[c]);
157 - }
158 -
159 - pid_t r = p->pid;
160 - p = p->next;
161 - del_pid_entry(r);
162 - }
163 - else {
164 - if(unlikely(p->keep)) p->keeploops++;
165 - p->keep = false;
166 - p = p->next;
167 - }
168 - }
169 -}
170 -
171 -// ----------------------------------------------------------------------------
172 -
173 -static inline void link_all_processes_to_their_parents(void) {
174 - struct pid_stat *p, *pp;
175 -
176 - // link all children to their parents
177 - // and update children count on parents
178 - for(p = root_of_pids; p ; p = p->next) {
179 - // for each process found
180 -
181 - p->sortlist = 0;
182 - p->parent = NULL;
183 -
184 - if(unlikely(!p->ppid)) {
185 - //unnecessary code from apps_plugin.c
186 - //p->parent = NULL;
187 - continue;
188 - }
189 -
190 - pp = all_pids[p->ppid];
191 - if(likely(pp)) {
192 - p->parent = pp;
193 - pp->children_count++;
194 -
195 - if(unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
196 - debug_log_int("child %d (%s, %s) on target '%s' has parent %d (%s, %s). Parent: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", gtime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", cgtime=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT "", p->pid, p->comm, p->updated?"running":"exited", (p->target)?p->target->name:"UNSET", pp->pid, pp->comm, pp->updated?"running":"exited", pp->utime, pp->stime, pp->gtime, pp->minflt, pp->majflt, pp->cutime, pp->cstime, pp->cgtime, pp->cminflt, pp->cmajflt);
197 - }
198 - else {
199 - p->parent = NULL;
200 - netdata_log_error("pid %d %s states parent %d, but the later does not exist.", p->pid, p->comm, p->ppid);
201 - }
202 - }
203 -}
204 -
205 -// ----------------------------------------------------------------------------
206 -
207 -static inline int debug_print_process_and_parents(struct pid_stat *p, usec_t time) {
208 - char *prefix = "\\_ ";
209 - int indent = 0;
210 -
211 - if(p->parent)
212 - indent = debug_print_process_and_parents(p->parent, p->stat_collected_usec);
213 - else
214 - prefix = " > ";
215 -
216 - char buffer[indent + 1];
217 - int i;
218 -
219 - for(i = 0; i < indent ;i++) buffer[i] = ' ';
220 - buffer[i] = '\0';
221 -
222 - fprintf(stderr, " %s %s%s (%d %s %"PRIu64""
223 - , buffer
224 - , prefix
225 - , p->comm
226 - , p->pid
227 - , p->updated?"running":"exited"
228 - , p->stat_collected_usec - time
229 - );
230 -
231 - if(p->utime) fprintf(stderr, " utime=" KERNEL_UINT_FORMAT, p->utime);
232 - if(p->stime) fprintf(stderr, " stime=" KERNEL_UINT_FORMAT, p->stime);
233 - if(p->gtime) fprintf(stderr, " gtime=" KERNEL_UINT_FORMAT, p->gtime);
234 - if(p->cutime) fprintf(stderr, " cutime=" KERNEL_UINT_FORMAT, p->cutime);
235 - if(p->cstime) fprintf(stderr, " cstime=" KERNEL_UINT_FORMAT, p->cstime);
236 - if(p->cgtime) fprintf(stderr, " cgtime=" KERNEL_UINT_FORMAT, p->cgtime);
237 - if(p->minflt) fprintf(stderr, " minflt=" KERNEL_UINT_FORMAT, p->minflt);
238 - if(p->cminflt) fprintf(stderr, " cminflt=" KERNEL_UINT_FORMAT, p->cminflt);
239 - if(p->majflt) fprintf(stderr, " majflt=" KERNEL_UINT_FORMAT, p->majflt);
240 - if(p->cmajflt) fprintf(stderr, " cmajflt=" KERNEL_UINT_FORMAT, p->cmajflt);
241 - fprintf(stderr, ")\n");
242 -
243 - return indent + 1;
244 -}
245 -
246 -static inline void debug_print_process_tree(struct pid_stat *p, char *msg __maybe_unused) {
247 - debug_log("%s: process %s (%d, %s) with parents:", msg, p->comm, p->pid, p->updated?"running":"exited");
248 - debug_print_process_and_parents(p, p->stat_collected_usec);
249 -}
250 -
251 -static inline void debug_find_lost_child(struct pid_stat *pe, kernel_uint_t lost, int type) {
252 - int found = 0;
253 - struct pid_stat *p = NULL;
254 -
255 - for(p = root_of_pids; p ; p = p->next) {
256 - if(p == pe) continue;
257 -
258 - switch(type) {
259 - case 1:
260 - if(p->cminflt > lost) {
261 - fprintf(stderr, " > process %d (%s) could use the lost exited child minflt " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
262 - found++;
263 - }
264 - break;
265 -
266 - case 2:
267 - if(p->cmajflt > lost) {
268 - fprintf(stderr, " > process %d (%s) could use the lost exited child majflt " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
269 - found++;
270 - }
271 - break;
272 -
273 - case 3:
274 - if(p->cutime > lost) {
275 - fprintf(stderr, " > process %d (%s) could use the lost exited child utime " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
276 - found++;
277 - }
278 - break;
279 -
280 - case 4:
281 - if(p->cstime > lost) {
282 - fprintf(stderr, " > process %d (%s) could use the lost exited child stime " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
283 - found++;
284 - }
285 - break;
286 -
287 - case 5:
288 - if(p->cgtime > lost) {
289 - fprintf(stderr, " > process %d (%s) could use the lost exited child gtime " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
290 - found++;
291 - }
292 - break;
293 - }
294 - }
295 -
296 - if(!found) {
297 - switch(type) {
298 - case 1:
299 - fprintf(stderr, " > cannot find any process to use the lost exited child minflt " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
300 - break;
301 -
302 - case 2:
303 - fprintf(stderr, " > cannot find any process to use the lost exited child majflt " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
304 - break;
305 -
306 - case 3:
307 - fprintf(stderr, " > cannot find any process to use the lost exited child utime " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
308 - break;
309 -
310 - case 4:
311 - fprintf(stderr, " > cannot find any process to use the lost exited child stime " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
312 - break;
313 -
314 - case 5:
315 - fprintf(stderr, " > cannot find any process to use the lost exited child gtime " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
316 - break;
317 - }
318 - }
319 -}
320 -
321 -static inline kernel_uint_t remove_exited_child_from_parent(kernel_uint_t *field, kernel_uint_t *pfield) {
322 - kernel_uint_t absorbed = 0;
323 -
324 - if(*field > *pfield) {
325 - absorbed += *pfield;
326 - *field -= *pfield;
327 - *pfield = 0;
328 - }
329 - else {
330 - absorbed += *field;
331 - *pfield -= *field;
332 - *field = 0;
333 - }
334 -
335 - return absorbed;
336 -}
337 -
338 -static inline void process_exited_pids() {
339 - struct pid_stat *p;
340 -
341 - for(p = root_of_pids; p ; p = p->next) {
342 - if(p->updated || !p->stat_collected_usec)
343 - continue;
344 -
345 - kernel_uint_t utime = (p->utime_raw + p->cutime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
346 - kernel_uint_t stime = (p->stime_raw + p->cstime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
347 - kernel_uint_t gtime = (p->gtime_raw + p->cgtime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
348 - kernel_uint_t minflt = (p->minflt_raw + p->cminflt_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
349 - kernel_uint_t majflt = (p->majflt_raw + p->cmajflt_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
350 -
351 - if(utime + stime + gtime + minflt + majflt == 0)
352 - continue;
353 -
354 - if(unlikely(debug_enabled)) {
355 - debug_log("Absorb %s (%d %s total resources: utime=" KERNEL_UINT_FORMAT " stime=" KERNEL_UINT_FORMAT " gtime=" KERNEL_UINT_FORMAT " minflt=" KERNEL_UINT_FORMAT " majflt=" KERNEL_UINT_FORMAT ")"
356 - , p->comm
357 - , p->pid
358 - , p->updated?"running":"exited"
359 - , utime
360 - , stime
361 - , gtime
362 - , minflt
363 - , majflt
364 - );
365 - debug_print_process_tree(p, "Searching parents");
366 - }
367 -
368 - struct pid_stat *pp;
369 - for(pp = p->parent; pp ; pp = pp->parent) {
370 - if(!pp->updated) continue;
371 -
372 - kernel_uint_t absorbed;
373 - absorbed = remove_exited_child_from_parent(&utime, &pp->cutime);
374 - if(unlikely(debug_enabled && absorbed))
375 - debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " utime (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, utime);
376 -
377 - absorbed = remove_exited_child_from_parent(&stime, &pp->cstime);
378 - if(unlikely(debug_enabled && absorbed))
379 - debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " stime (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, stime);
380 -
381 - absorbed = remove_exited_child_from_parent(&gtime, &pp->cgtime);
382 - if(unlikely(debug_enabled && absorbed))
383 - debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " gtime (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, gtime);
384 -
385 - absorbed = remove_exited_child_from_parent(&minflt, &pp->cminflt);
386 - if(unlikely(debug_enabled && absorbed))
387 - debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " minflt (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, minflt);
388 -
389 - absorbed = remove_exited_child_from_parent(&majflt, &pp->cmajflt);
390 - if(unlikely(debug_enabled && absorbed))
391 - debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " majflt (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, majflt);
392 - }
393 -
394 - if(unlikely(utime + stime + gtime + minflt + majflt > 0)) {
395 - if(unlikely(debug_enabled)) {
396 - if(utime) debug_find_lost_child(p, utime, 3);
397 - if(stime) debug_find_lost_child(p, stime, 4);
398 - if(gtime) debug_find_lost_child(p, gtime, 5);
399 - if(minflt) debug_find_lost_child(p, minflt, 1);
400 - if(majflt) debug_find_lost_child(p, majflt, 2);
401 - }
402 -
403 - p->keep = true;
404 -
405 - debug_log(" > remaining resources - KEEP - for another loop: %s (%d %s total resources: utime=" KERNEL_UINT_FORMAT " stime=" KERNEL_UINT_FORMAT " gtime=" KERNEL_UINT_FORMAT " minflt=" KERNEL_UINT_FORMAT " majflt=" KERNEL_UINT_FORMAT ")"
406 - , p->comm
407 - , p->pid
408 - , p->updated?"running":"exited"
409 - , utime
410 - , stime
411 - , gtime
412 - , minflt
413 - , majflt
414 - );
415 -
416 - for(pp = p->parent; pp ; pp = pp->parent) {
417 - if(pp->updated) break;
418 - pp->keep = true;
419 -
420 - debug_log(" > - KEEP - parent for another loop: %s (%d %s)"
421 - , pp->comm
422 - , pp->pid
423 - , pp->updated?"running":"exited"
424 - );
425 - }
426 -
427 - p->utime_raw = utime * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
428 - p->stime_raw = stime * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
429 - p->gtime_raw = gtime * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
430 - p->minflt_raw = minflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
431 - p->majflt_raw = majflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
432 - p->cutime_raw = p->cstime_raw = p->cgtime_raw = p->cminflt_raw = p->cmajflt_raw = 0;
433 -
434 - debug_log(" ");
435 - }
436 - else
437 - debug_log(" > totally absorbed - DONE - %s (%d %s)"
438 - , p->comm
439 - , p->pid
440 - , p->updated?"running":"exited"
441 - );
442 - }
443 -}
444 -
445 -// ----------------------------------------------------------------------------
446 -
447 -// 1. read all files in /proc
448 -// 2. for each numeric directory:
449 -// i. read /proc/pid/stat
450 -// ii. read /proc/pid/status
451 -// iii. read /proc/pid/io (requires root access)
452 -// iii. read the entries in directory /proc/pid/fd (requires root access)
453 -// for each entry:
454 -// a. find or create a struct file_descriptor
455 -// b. cleanup any old/unused file_descriptors
456 -
457 -// after all these, some pids may be linked to targets, while others may not
458 -
459 -// in case of errors, only 1 every 1000 errors is printed
460 -// to avoid filling up all disk space
461 -// if debug is enabled, all errors are printed
462 -
463 -static inline void mark_pid_as_unread(struct pid_stat *p) {
464 - p->read = false; // mark it as not read, so that collect_data_for_pid() will read it
465 - p->updated = false;
466 - p->merged = false;
467 - p->children_count = 0;
468 - p->parent = NULL;
469 -}
470 -
471 -#if defined(__FreeBSD__) || defined(__APPLE__)
472 -static inline void get_current_time(void) {
473 - struct timeval current_time;
474 - gettimeofday(&current_time, NULL);
475 - system_current_time_ut = timeval_usec(&current_time);
476 -}
477 -#endif
478 -
479 -#if defined(__FreeBSD__)
480 -static inline bool collect_data_for_all_pids_per_os(void) {
481 - // Mark all processes as unread before collecting new data
482 - struct pid_stat *p = NULL;
483 - if(all_pids_count) {
484 - for(p = root_of_pids; p ; p = p->next)
485 - mark_pid_as_unread(p);
486 - }
487 -
488 - int i, procnum;
489 -
490 - static size_t procbase_size = 0;
491 - static struct kinfo_proc *procbase = NULL;
492 -
493 - size_t new_procbase_size;
494 -
495 - int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC };
496 - if (unlikely(sysctl(mib, 3, NULL, &new_procbase_size, NULL, 0))) {
497 - netdata_log_error("sysctl error: Can't get processes data size");
498 - return false;
499 - }
500 -
501 - // give it some air for processes that may be started
502 - // during this little time.
503 - new_procbase_size += 100 * sizeof(struct kinfo_proc);
504 -
505 - // increase the buffer if needed
506 - if(new_procbase_size > procbase_size) {
507 - procbase_size = new_procbase_size;
508 - procbase = reallocz(procbase, procbase_size);
509 - }
510 -
511 - // sysctl() gets from new_procbase_size the buffer size
512 - // and also returns to it the amount of data filled in
513 - new_procbase_size = procbase_size;
514 -
515 - // get the processes from the system
516 - if (unlikely(sysctl(mib, 3, procbase, &new_procbase_size, NULL, 0))) {
517 - netdata_log_error("sysctl error: Can't get processes data");
518 - return false;
519 - }
520 -
521 - // based on the amount of data filled in
522 - // calculate the number of processes we got
523 - procnum = new_procbase_size / sizeof(struct kinfo_proc);
524 -
525 - get_current_time();
526 -
527 - for (i = 0 ; i < procnum ; ++i) {
528 - pid_t pid = procbase[i].ki_pid;
529 - if (pid <= 0) continue;
530 - collect_data_for_pid(pid, &procbase[i]);
531 - }
532 -
533 - return true;
534 -}
535 -#endif // __FreeBSD__
536 -
537 -#if defined(__APPLE__)
538 -static inline bool collect_data_for_all_pids_per_os(void) {
539 - // Mark all processes as unread before collecting new data
540 - struct pid_stat *p;
541 - if(all_pids_count) {
542 - for(p = root_of_pids; p; p = p->next)
543 - mark_pid_as_unread(p);
544 - }
545 -
546 - static pid_t *pids = NULL;
547 - static int allocatedProcessCount = 0;
548 -
549 - // Get the number of processes
550 - int numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
551 - if (numberOfProcesses <= 0) {
552 - netdata_log_error("Failed to retrieve the process count");
553 - return false;
554 - }
555 -
556 - // Allocate or reallocate space to hold all the process IDs if necessary
557 - if (numberOfProcesses > allocatedProcessCount) {
558 - // Allocate additional space to avoid frequent reallocations
559 - allocatedProcessCount = numberOfProcesses + 100;
560 - pids = reallocz(pids, allocatedProcessCount * sizeof(pid_t));
561 - }
562 -
563 - // this is required, otherwise the PIDs become totally random
564 - memset(pids, 0, allocatedProcessCount * sizeof(pid_t));
565 -
566 - // get the list of PIDs
567 - numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, pids, allocatedProcessCount * sizeof(pid_t));
568 - if (numberOfProcesses <= 0) {
569 - netdata_log_error("Failed to retrieve the process IDs");
570 - return false;
571 - }
572 -
573 - get_current_time();
574 -
575 - // Collect data for each process
576 - for (int i = 0; i < numberOfProcesses; ++i) {
577 - pid_t pid = pids[i];
578 - if (pid <= 0) continue;
579 -
580 - struct pid_info pi = { 0 };
581 -
582 - int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
583 -
584 - size_t procSize = sizeof(pi.proc);
585 - if(sysctl(mib, 4, &pi.proc, &procSize, NULL, 0) == -1) {
586 - netdata_log_error("Failed to get proc for PID %d", pid);
587 - continue;
588 - }
589 - if(procSize == 0) // no such process
590 - continue;
591 -
592 - int st = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pi.taskinfo, sizeof(pi.taskinfo));
593 - if (st <= 0) {
594 - netdata_log_error("Failed to get task info for PID %d", pid);
595 - continue;
596 - }
597 -
598 - st = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &pi.bsdinfo, sizeof(pi.bsdinfo));
599 - if (st <= 0) {
600 - netdata_log_error("Failed to get BSD info for PID %d", pid);
601 - continue;
602 - }
603 -
604 - st = proc_pid_rusage(pid, RUSAGE_INFO_V4, (rusage_info_t *)&pi.rusageinfo);
605 - if (st < 0) {
606 - netdata_log_error("Failed to get resource usage info for PID %d", pid);
607 - continue;
608 - }
609 -
610 - collect_data_for_pid(pid, &pi);
611 - }
612 -
613 - return true;
614 -}
615 -#endif // __APPLE__
616 -
617 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
618 -static int compar_pid(const void *pid1, const void *pid2) {
619 -
620 - struct pid_stat *p1 = all_pids[*((pid_t *)pid1)];
621 - struct pid_stat *p2 = all_pids[*((pid_t *)pid2)];
622 -
623 - if(p1->sortlist > p2->sortlist)
624 - return -1;
625 - else
626 - return 1;
627 -}
628 -
629 -static inline bool collect_data_for_all_pids_per_os(void) {
630 - struct pid_stat *p = NULL;
631 -
632 - // clear process state counter
633 - memset(proc_state_count, 0, sizeof proc_state_count);
634 -
635 - if(all_pids_count) {
636 - size_t slc = 0;
637 - for(p = root_of_pids; p ; p = p->next) {
638 - mark_pid_as_unread(p);
639 - all_pids_sortlist[slc++] = p->pid;
640 - }
641 -
642 - if(unlikely(slc != all_pids_count)) {
643 - netdata_log_error("Internal error: I was thinking I had %zu processes in my arrays, but it seems there are %zu.", all_pids_count, slc);
644 - all_pids_count = slc;
645 - }
646 -
647 - if(include_exited_childs) {
648 - // Read parents before childs
649 - // This is needed to prevent a situation where
650 - // a child is found running, but until we read
651 - // its parent, it has exited and its parent
652 - // has accumulated its resources.
653 -
654 - qsort((void *)all_pids_sortlist, (size_t)all_pids_count, sizeof(pid_t), compar_pid);
655 -
656 - // we forward read all running processes
657 - // collect_data_for_pid() is smart enough,
658 - // not to read the same pid twice per iteration
659 - for(slc = 0; slc < all_pids_count; slc++) {
660 - collect_data_for_pid(all_pids_sortlist[slc], NULL);
661 - }
662 - }
663 - }
664 -
665 - static char uptime_filename[FILENAME_MAX + 1] = "";
666 - if(*uptime_filename == '\0')
667 - snprintfz(uptime_filename, FILENAME_MAX, "%s/proc/uptime", netdata_configured_host_prefix);
668 -
669 - system_uptime_secs = (kernel_uint_t)(uptime_msec(uptime_filename) / MSEC_PER_SEC);
670 -
671 - char dirname[FILENAME_MAX + 1];
672 -
673 - snprintfz(dirname, FILENAME_MAX, "%s/proc", netdata_configured_host_prefix);
674 - DIR *dir = opendir(dirname);
675 - if(!dir) return false;
676 -
677 - struct dirent *de = NULL;
678 -
679 - while((de = readdir(dir))) {
680 - char *endptr = de->d_name;
681 -
682 - if(unlikely(de->d_type != DT_DIR || de->d_name[0] < '0' || de->d_name[0] > '9'))
683 - continue;
684 -
685 - pid_t pid = (pid_t) strtoul(de->d_name, &endptr, 10);
686 -
687 - // make sure we read a valid number
688 - if(unlikely(endptr == de->d_name || *endptr != '\0'))
689 - continue;
690 -
691 - collect_data_for_pid(pid, NULL);
692 - }
693 - closedir(dir);
694 -
695 - return true;
696 -}
697 -#endif // !__FreeBSD__ && !__APPLE__
698 -
699 -bool collect_data_for_all_pids(void) {
700 - if(!collect_data_for_all_pids_per_os())
701 - return false;
702 -
703 - if(!all_pids_count)
704 - return false;
705 -
706 - // we need /proc/stat to normalize the cpu consumption of the exited childs
707 - read_global_time();
708 -
709 - // build the process tree
710 - link_all_processes_to_their_parents();
711 -
712 - // normally this is done
713 - // however we may have processes exited while we collected values
714 - // so let's find the exited ones
715 - // we do this by collecting the ownership of process
716 - // if we manage to get the ownership, the process still runs
717 - process_exited_pids();
718 -
719 - return true;
720 -}
src/collectors/apps.plugin/apps_proc_stat.c deleted
-154
@@ -1,154 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "apps_plugin.h"
4 -
5 -#if defined(__APPLE__)
6 -int read_global_time(void) {
7 - static kernel_uint_t utime_raw = 0, stime_raw = 0, ntime_raw = 0;
8 - static usec_t collected_usec = 0, last_collected_usec = 0;
9 -
10 - host_cpu_load_info_data_t cpuinfo;
11 - mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
12 -
13 - if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpuinfo, &count) != KERN_SUCCESS) {
14 - // Handle error
15 - goto cleanup;
16 - }
17 -
18 - last_collected_usec = collected_usec;
19 - collected_usec = now_monotonic_usec();
20 -
21 - calls_counter++;
22 -
23 - // Convert ticks to time
24 - // Note: MacOS does not separate nice time from user time in the CPU stats, so you might need to adjust this logic
25 - kernel_uint_t global_ntime = 0; // Assuming you want to keep track of nice time separately
26 -
27 - incremental_rate(global_utime, utime_raw, cpuinfo.cpu_ticks[CPU_STATE_USER] + cpuinfo.cpu_ticks[CPU_STATE_NICE], collected_usec, last_collected_usec);
28 - incremental_rate(global_ntime, ntime_raw, cpuinfo.cpu_ticks[CPU_STATE_NICE], collected_usec, last_collected_usec);
29 - incremental_rate(global_stime, stime_raw, cpuinfo.cpu_ticks[CPU_STATE_SYSTEM], collected_usec, last_collected_usec);
30 -
31 - global_utime += global_ntime;
32 -
33 - if(unlikely(global_iterations_counter == 1)) {
34 - global_utime = 0;
35 - global_stime = 0;
36 - global_gtime = 0;
37 - }
38 -
39 - return 1;
40 -
41 -cleanup:
42 - global_utime = 0;
43 - global_stime = 0;
44 - global_gtime = 0;
45 - return 0;
46 -}
47 -#endif // __APPLE__
48 -
49 -
50 -#if defined(__FreeBSD__)
51 -int read_global_time(void) {
52 - static kernel_uint_t utime_raw = 0, stime_raw = 0, ntime_raw = 0;
53 - static usec_t collected_usec = 0, last_collected_usec = 0;
54 - long cp_time[CPUSTATES];
55 -
56 - if (unlikely(CPUSTATES != 5)) {
57 - goto cleanup;
58 - } else {
59 - static int mib[2] = {0, 0};
60 -
61 - if (unlikely(GETSYSCTL_SIMPLE("kern.cp_time", mib, cp_time))) {
62 - goto cleanup;
63 - }
64 - }
65 -
66 - last_collected_usec = collected_usec;
67 - collected_usec = now_monotonic_usec();
68 -
69 - calls_counter++;
70 -
71 - // temporary - it is added global_ntime;
72 - kernel_uint_t global_ntime = 0;
73 -
74 - incremental_rate(global_utime, utime_raw, cp_time[0] * 100LLU / system_hz, collected_usec, last_collected_usec);
75 - incremental_rate(global_ntime, ntime_raw, cp_time[1] * 100LLU / system_hz, collected_usec, last_collected_usec);
76 - incremental_rate(global_stime, stime_raw, cp_time[2] * 100LLU / system_hz, collected_usec, last_collected_usec);
77 -
78 - global_utime += global_ntime;
79 -
80 - if(unlikely(global_iterations_counter == 1)) {
81 - global_utime = 0;
82 - global_stime = 0;
83 - global_gtime = 0;
84 - }
85 -
86 - return 1;
87 -
88 -cleanup:
89 - global_utime = 0;
90 - global_stime = 0;
91 - global_gtime = 0;
92 - return 0;
93 -}
94 -#endif // __APPLE__
95 -
96 -#if !defined(__FreeBSD__) && !defined(__APPLE__)
97 -int read_global_time(void) {
98 - static char filename[FILENAME_MAX + 1] = "";
99 - static procfile *ff = NULL;
100 - static kernel_uint_t utime_raw = 0, stime_raw = 0, gtime_raw = 0, gntime_raw = 0, ntime_raw = 0;
101 - static usec_t collected_usec = 0, last_collected_usec = 0;
102 -
103 - if(unlikely(!ff)) {
104 - snprintfz(filename, FILENAME_MAX, "%s/proc/stat", netdata_configured_host_prefix);
105 - ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
106 - if(unlikely(!ff)) goto cleanup;
107 - }
108 -
109 - ff = procfile_readall(ff);
110 - if(unlikely(!ff)) goto cleanup;
111 -
112 - last_collected_usec = collected_usec;
113 - collected_usec = now_monotonic_usec();
114 -
115 - calls_counter++;
116 -
117 - // temporary - it is added global_ntime;
118 - kernel_uint_t global_ntime = 0;
119 -
120 - incremental_rate(global_utime, utime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 1)), collected_usec, last_collected_usec);
121 - incremental_rate(global_ntime, ntime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 2)), collected_usec, last_collected_usec);
122 - incremental_rate(global_stime, stime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 3)), collected_usec, last_collected_usec);
123 - incremental_rate(global_gtime, gtime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 10)), collected_usec, last_collected_usec);
124 -
125 - global_utime += global_ntime;
126 -
127 - if(enable_guest_charts) {
128 - // temporary - it is added global_ntime;
129 - kernel_uint_t global_gntime = 0;
130 -
131 - // guest nice time, on guest time
132 - incremental_rate(global_gntime, gntime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 11)), collected_usec, last_collected_usec);
133 -
134 - global_gtime += global_gntime;
135 -
136 - // remove guest time from user time
137 - global_utime -= (global_utime > global_gtime) ? global_gtime : global_utime;
138 - }
139 -
140 - if(unlikely(global_iterations_counter == 1)) {
141 - global_utime = 0;
142 - global_stime = 0;
143 - global_gtime = 0;
144 - }
145 -
146 - return 1;
147 -
148 -cleanup:
149 - global_utime = 0;
150 - global_stime = 0;
151 - global_gtime = 0;
152 - return 0;
153 -}
154 -#endif // !__FreeBSD__ !__APPLE__
src/collectors/apps.plugin/apps_targets.c
+328 -97
@@ -2,22 +2,226 @@
2
3 #include "apps_plugin.h"
4
5 -// ----------------------------------------------------------------------------
6 -// apps_groups.conf
7 -// aggregate all processes in groups, to have a limited number of dimensions
5 +pid_t INIT_PID = OS_INIT_PID;
6 +
7 +static STRING *get_clean_name(STRING *name) {
8 + char buf[string_strlen(name) + 1];
9 + memcpy(buf, string2str(name), string_strlen(name) + 1);
10 + netdata_fix_chart_name(buf);
11 + for (char *d = buf; *d ; d++) {
12 + if (*d == '.') *d = '_';
13 + }
14 + return string_strdupz(buf);
15 +}
16 +
17 +static inline STRING *get_numeric_string(uint64_t n) {
18 + char buf[UINT64_MAX_LENGTH];
19 + print_uint64(buf, n);
20 + return string_strdupz(buf);
21 +}
22 +
23 +struct target *find_target_by_name(struct target *base, const char *name) {
24 + struct target *t;
25 + for(t = base; t ; t = t->next) {
26 + if (string_strcmp(t->name, name) == 0)
27 + return t;
28 + }
29 +
30 + return NULL;
31 +}
32 +
33 +// --------------------------------------------------------------------------------------------------------------------
34 +// Tree
35 +
36 +static inline STRING *comm_from_cmdline(STRING *comm, STRING *cmdline) {
37 + if(!cmdline) return sanitize_chart_meta_string(comm);
38 +
39 + const char *cl = string2str(cmdline);
40 + size_t len = string_strlen(cmdline);
41 +
42 + char buf_cmd[len + 1];
43 + // if it is enclosed in (), remove the parenthesis
44 + if(cl[0] == '(' && cl[len - 1] == ')') {
45 + memcpy(buf_cmd, &cl[1], len - 2);
46 + buf_cmd[len - 2] = '\0';
47 + }
48 + else
49 + memcpy(buf_cmd, cl, sizeof(buf_cmd));
50 +
51 + char *start = strstr(buf_cmd, string2str(comm));
52 + if(start) {
53 + char *end = start + string_strlen(comm);
54 + while(*end && !isspace((uint8_t)*end) && *end != '/' && *end != '\\') end++;
55 + *end = '\0';
56 +
57 + sanitize_chart_meta(start);
58 + return string_strdupz(start);
59 + }
60 +
61 + return sanitize_chart_meta_string(comm);
62 +}
63 +
64 +struct comm_list {
65 + STRING *comm;
66 +};
67 +
68 +struct managed_list {
69 + size_t used;
70 + size_t size;
71 + struct comm_list *array;
72 +};
73 +
74 +static struct {
75 + struct managed_list managers;
76 + struct managed_list aggregators;
77 +} tree = {
78 + .managers = {
79 + .array = NULL,
80 + .size = 0,
81 + .used = 0,
82 + },
83 + .aggregators = {
84 + .array = NULL,
85 + .size = 0,
86 + .used = 0,
87 + }
88 +};
89 +
90 +static void managed_list_clear(struct managed_list *list) {
91 + for(size_t c = 0; c < list->used ; c++)
92 + string_freez(list->array[c].comm);
93 +
94 + freez(list->array);
95 + list->array = NULL;
96 + list->used = 0;
97 + list->size = 0;
98 +}
99 +
100 +static void managed_list_add(struct managed_list *list, const char *s) {
101 + if(list->used >= list->size) {
102 + if(!list->size)
103 + list->size = 10;
104 + else
105 + list->size *= 2;
106 + list->array = reallocz(list->array, sizeof(*list->array) * list->size);
107 + }
108 +
109 + list->array[list->used++].comm = string_strdupz(s);
110 +}
111 +
112 +static STRING *KernelAggregator = NULL;
113 +
114 +void apps_orchestrators_and_aggregators_init(void) {
115 + KernelAggregator = string_strdupz("kernel");
116 +
117 + managed_list_clear(&tree.managers);
118 +#if defined(OS_LINUX)
119 + managed_list_add(&tree.managers, "init"); // linux systems
120 + managed_list_add(&tree.managers, "systemd"); // lxc containers and host systems (this also catches "systemd --user")
121 + managed_list_add(&tree.managers, "containerd-shim"); // docker containers
122 + managed_list_add(&tree.managers, "docker-init"); // docker containers
123 + managed_list_add(&tree.managers, "dumb-init"); // some docker containers use this
124 + managed_list_add(&tree.managers, "gnome-shell"); // gnome user applications
125 +#elif defined(OS_WINDOWS)
126 + managed_list_add(&tree.managers, "System");
127 + managed_list_add(&tree.managers, "services");
128 + managed_list_add(&tree.managers, "wininit");
129 +#elif defined(OS_FREEBSD)
130 + managed_list_add(&tree.managers, "init");
131 +#elif defined(OS_MACOS)
132 + managed_list_add(&tree.managers, "launchd");
133 +#endif
134 +
135 + managed_list_clear(&tree.aggregators);
136 +#if defined(OS_LINUX)
137 + managed_list_add(&tree.aggregators, "kthread");
138 +#elif defined(OS_WINDOWS)
139 +#elif defined(OS_FREEBSD)
140 + managed_list_add(&tree.aggregators, "kernel");
141 +#elif defined(OS_MACOS)
142 +#endif
143 +}
144 +
145 +static inline bool is_orchestrator(struct pid_stat *p) {
146 + for(size_t c = 0; c < tree.managers.used ; c++) {
147 + if(p->comm == tree.managers.array[c].comm)
148 + return true;
149 + }
150 +
151 + return false;
152 +}
153 +
154 +static inline bool is_aggregator(struct pid_stat *p) {
155 + for(size_t c = 0; c < tree.aggregators.used ; c++) {
156 + if(p->comm == tree.aggregators.array[c].comm)
157 + return true;
158 + }
159 +
160 + return false;
161 +}
162 +
163 +struct target *get_tree_target(struct pid_stat *p) {
164 +// // skip fast all the children that are more than 3 levels down
165 +// while(p->parent && p->parent->pid != INIT_PID && p->parent->parent && p->parent->parent->parent)
166 +// p = p->parent;
167 +
168 + // keep the children of INIT_PID, and process orchestrators
169 + while(p->parent && p->parent->pid != INIT_PID && p->parent->pid != 0 && !is_orchestrator(p->parent))
170 + p = p->parent;
171 +
172 + // merge all processes into process aggregators
173 + STRING *search_for = string_dup(p->comm);
174 + bool aggregator = false;
175 + if((p->ppid == 0 && p->pid != INIT_PID) || (p->parent && is_aggregator(p->parent))) {
176 + aggregator = true;
177 + search_for = string_dup(KernelAggregator);
178 + }
179 +
180 + if(!aggregator) {
181 +#if (PROCESSES_HAVE_COMM_AND_NAME == 1)
182 + search_for = sanitize_chart_meta_string(p->name ? p->name : p->comm);
183 +#else
184 + search_for = comm_from_cmdline(p->comm, p->cmdline);
185 +#endif
186 + }
187 +
188 + struct target *w;
189 + for(w = apps_groups_root_target; w ; w = w->next) {
190 + if (w->name == search_for) {
191 + string_freez(search_for);
192 + return w;
193 + }
194 + }
195 +
196 + w = callocz(sizeof(struct target), 1);
197 + w->type = TARGET_TYPE_TREE;
198 + w->starts_with = w->ends_with = false;
199 + w->compare = string_dup(p->comm);
200 + w->id = search_for;
201 + w->name = string_dup(search_for);
202 + w->clean_name = get_clean_name(w->name);
203 +
204 + w->next = apps_groups_root_target;
205 + apps_groups_root_target = w;
206
9 -struct target *get_users_target(uid_t uid) {
207 + return w;
208 +}
209 +
210 +// --------------------------------------------------------------------------------------------------------------------
211 +// Users
212 +
213 +#if (PROCESSES_HAVE_UID == 1)
214 +struct target *users_root_target = NULL;
215 +
216 +struct target *get_uid_target(uid_t uid) {
217 struct target *w;
218 for(w = users_root_target ; w ; w = w->next)
219 if(w->uid == uid) return w;
220
221 w = callocz(sizeof(struct target), 1);
15 - snprintfz(w->compare, MAX_COMPARE_NAME, "%u", uid);
16 - w->comparehash = simple_hash(w->compare);
17 - w->comparelen = strlen(w->compare);
18 -
19 - snprintfz(w->id, MAX_NAME, "%u", uid);
20 - w->idhash = simple_hash(w->id);
222 + w->type = TARGET_TYPE_UID;
223 + w->uid = uid;
224 + w->id = get_numeric_string(uid);
225
226 struct user_or_group_id user_id_to_find = {
227 .id = {
@@ -27,41 +231,41 @@ struct target *get_users_target(uid_t uid) {
231 struct user_or_group_id *user_or_group_id = user_id_find(&user_id_to_find);
232
233 if(user_or_group_id && user_or_group_id->name && *user_or_group_id->name)
30 - snprintfz(w->name, MAX_NAME, "%s", user_or_group_id->name);
31 -
234 + w->name = string_strdupz(user_or_group_id->name);
235 else {
236 struct passwd *pw = getpwuid(uid);
237 if(!pw || !pw->pw_name || !*pw->pw_name)
35 - snprintfz(w->name, MAX_NAME, "%u", uid);
238 + w->name = get_numeric_string(uid);
239 else
37 - snprintfz(w->name, MAX_NAME, "%s", pw->pw_name);
240 + w->name = string_strdupz(pw->pw_name);
241 }
242
40 - strncpyz(w->clean_name, w->name, MAX_NAME);
41 - netdata_fix_chart_name(w->clean_name);
42 -
43 - w->uid = uid;
243 + w->clean_name = get_clean_name(w->name);
244
245 w->next = users_root_target;
246 users_root_target = w;
247
48 - debug_log("added uid %u ('%s') target", w->uid, w->name);
248 + debug_log("added uid %u ('%s') target", w->uid, string2str(w->name));
249
250 return w;
251 }
252 +#endif
253 +
254 +// --------------------------------------------------------------------------------------------------------------------
255 +// Groups
256
53 -struct target *get_groups_target(gid_t gid) {
257 +#if (PROCESSES_HAVE_GID == 1)
258 +struct target *groups_root_target = NULL;
259 +
260 +struct target *get_gid_target(gid_t gid) {
261 struct target *w;
262 for(w = groups_root_target ; w ; w = w->next)
263 if(w->gid == gid) return w;
264
265 w = callocz(sizeof(struct target), 1);
59 - snprintfz(w->compare, MAX_COMPARE_NAME, "%u", gid);
60 - w->comparehash = simple_hash(w->compare);
61 - w->comparelen = strlen(w->compare);
62 -
63 - snprintfz(w->id, MAX_NAME, "%u", gid);
64 - w->idhash = simple_hash(w->id);
266 + w->type = TARGET_TYPE_GID;
267 + w->gid = gid;
268 + w->id = get_numeric_string(gid);
269
270 struct user_or_group_id group_id_to_find = {
271 .id = {
@@ -70,21 +274,17 @@ struct target *get_groups_target(gid_t gid) {
274 };
275 struct user_or_group_id *group_id = group_id_find(&group_id_to_find);
276
73 - if(group_id && group_id->name && *group_id->name) {
74 - snprintfz(w->name, MAX_NAME, "%s", group_id->name);
75 - }
277 + if(group_id && group_id->name)
278 + w->name = string_strdupz(group_id->name);
279 else {
280 struct group *gr = getgrgid(gid);
281 if(!gr || !gr->gr_name || !*gr->gr_name)
79 - snprintfz(w->name, MAX_NAME, "%u", gid);
282 + w->name = get_numeric_string(gid);
283 else
81 - snprintfz(w->name, MAX_NAME, "%s", gr->gr_name);
284 + w->name = string_strdupz(gr->gr_name);
285 }
286
84 - strncpyz(w->clean_name, w->name, MAX_NAME);
85 - netdata_fix_chart_name(w->clean_name);
86 -
87 - w->gid = gid;
287 + w->clean_name = get_clean_name(w->name);
288
289 w->next = groups_root_target;
290 groups_root_target = w;
@@ -93,87 +293,101 @@ struct target *get_groups_target(gid_t gid) {
293
294 return w;
295 }
296 +#endif
297 +
298 +// --------------------------------------------------------------------------------------------------------------------
299 +// apps_groups.conf
300 +
301 +struct target *apps_groups_root_target = NULL;
302
303 // find or create a new target
304 // there are targets that are just aggregated to other target (the second argument)
305 static struct target *get_apps_groups_target(const char *id, struct target *target, const char *name) {
100 - int tdebug = 0, thidden = target?target->hidden:0, ends_with = 0;
101 - const char *nid = id;
102 -
103 - // extract the options
104 - while(nid[0] == '-' || nid[0] == '+' || nid[0] == '*') {
105 - if(nid[0] == '-') thidden = 1;
106 - if(nid[0] == '+') tdebug = 1;
107 - if(nid[0] == '*') ends_with = 1;
108 - nid++;
306 + bool tdebug = false, thidden = target ? target->hidden : false, ends_with = false, starts_with = false;
307 +
308 + STRING *id_lookup = NULL;
309 + STRING *name_lookup = NULL;
310 +
311 + // extract the options from the id
312 + {
313 + size_t len = strlen(id);
314 + char buf[len + 1];
315 + memcpy(buf, id, sizeof(buf));
316 +
317 + if(buf[len - 1] == '*') {
318 + buf[--len] = '\0';
319 + starts_with = true;
320 + }
321 +
322 + const char *nid = buf;
323 + while (nid[0] == '-' || nid[0] == '+' || nid[0] == '*') {
324 + if (nid[0] == '-') thidden = true;
325 + if (nid[0] == '+') tdebug = true;
326 + if (nid[0] == '*') ends_with = true;
327 + nid++;
328 + }
329 +
330 + id_lookup = string_strdupz(nid);
331 + }
332 +
333 + // extract the options from the name
334 + {
335 + size_t len = strlen(name);
336 + char buf[len + 1];
337 + memcpy(buf, name, sizeof(buf));
338 +
339 + const char *nn = buf;
340 + while (nn[0] == '-' || nn[0] == '+') {
341 + if (nn[0] == '-') thidden = true;
342 + if (nn[0] == '+') tdebug = true;
343 + nn++;
344 + }
345 +
346 + name_lookup = string_strdupz(nn);
347 }
110 - uint32_t hash = simple_hash(id);
348
349 // find if it already exists
350 struct target *w, *last = apps_groups_root_target;
351 for(w = apps_groups_root_target ; w ; w = w->next) {
115 - if(w->idhash == hash && strncmp(nid, w->id, MAX_NAME) == 0)
352 + if(w->id == id_lookup) {
353 + string_freez(id_lookup);
354 + string_freez(name_lookup);
355 return w;
356 + }
357
358 last = w;
359 }
360
361 // find an existing target
362 if(unlikely(!target)) {
123 - while(*name == '-') {
124 - if(*name == '-') thidden = 1;
125 - name++;
126 - }
127 -
128 - for(target = apps_groups_root_target ; target != NULL ; target = target->next) {
129 - if(!target->target && strcmp(name, target->name) == 0)
363 + for(target = apps_groups_root_target ; target ; target = target->next) {
364 + if(!target->target && name_lookup == target->name)
365 break;
366 }
132 -
133 - if(unlikely(debug_enabled)) {
134 - if(unlikely(target))
135 - debug_log("REUSING TARGET NAME '%s' on ID '%s'", target->name, target->id);
136 - else
137 - debug_log("NEW TARGET NAME '%s' on ID '%s'", name, id);
138 - }
367 }
368
369 if(target && target->target)
142 - fatal("Internal Error: request to link process '%s' to target '%s' which is linked to target '%s'", id, target->id, target->target->id);
370 + fatal("Internal Error: request to link process '%s' to target '%s' which is linked to target '%s'",
371 + id, string2str(target->id), string2str(target->target->id));
372
373 w = callocz(sizeof(struct target), 1);
145 - strncpyz(w->id, nid, MAX_NAME);
146 - w->idhash = simple_hash(w->id);
374 + w->type = TARGET_TYPE_APP_GROUP;
375 + w->compare = string_dup(id_lookup);
376 + w->starts_with = starts_with;
377 + w->ends_with = ends_with;
378 + w->id = string_dup(id_lookup);
379
380 if(unlikely(!target))
149 - // copy the name
150 - strncpyz(w->name, name, MAX_NAME);
381 + w->name = string_dup(name_lookup); // copy the name
382 else
152 - // copy the id
153 - strncpyz(w->name, nid, MAX_NAME);
383 + w->name = string_dup(id_lookup); // copy the id
384
385 // dots are used to distinguish chart type and id in streaming, so we should replace them
156 - strncpyz(w->clean_name, w->name, MAX_NAME);
157 - netdata_fix_chart_name(w->clean_name);
158 - for (char *d = w->clean_name; *d; d++) {
159 - if (*d == '.')
160 - *d = '_';
161 - }
162 -
163 - strncpyz(w->compare, nid, MAX_COMPARE_NAME);
164 - size_t len = strlen(w->compare);
165 - if(w->compare[len - 1] == '*') {
166 - w->compare[len - 1] = '\0';
167 - w->starts_with = 1;
168 - }
169 - w->ends_with = ends_with;
386 + w->clean_name = get_clean_name(w->name);
387
388 if(w->starts_with && w->ends_with)
389 proc_pid_cmdline_is_needed = true;
390
174 - w->comparehash = simple_hash(w->compare);
175 - w->comparelen = strlen(w->compare);
176 -
391 w->hidden = thidden;
392 #ifdef NETDATA_INTERNAL_CHECKS
393 w->debug_enabled = tdebug;
@@ -188,13 +402,17 @@ static struct target *get_apps_groups_target(const char *id, struct target *targ
402 else apps_groups_root_target = w;
403
404 debug_log("ADDING TARGET ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s"
191 - , w->id
192 - , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
193 - , w->target?w->target->name:w->name
405 + , string2str(w->id)
406 + , string2str(w->compare)
407 + , (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
408 + , w->target?w->target->name:w->name
409 , (w->hidden)?"hidden":"-"
410 , (w->debug_enabled)?"debug":"-"
411 );
412
413 + string_freez(id_lookup);
414 + string_freez(name_lookup);
415 +
416 return w;
417 }
418
@@ -219,6 +437,8 @@ int read_apps_groups_conf(const char *path, const char *file) {
437
438 size_t line, lines = procfile_lines(ff);
439
440 + bool managers_reset_done = false;
441 +
442 for(line = 0; line < lines ;line++) {
443 size_t word, words = procfile_linewords(ff, line);
444 if(!words) continue;
@@ -226,6 +446,27 @@ int read_apps_groups_conf(const char *path, const char *file) {
446 char *name = procfile_lineword(ff, line, 0);
447 if(!name || !*name) continue;
448
449 + if(strcmp(name, "managers") == 0) {
450 + if(!managers_reset_done) {
451 + managers_reset_done = true;
452 + managed_list_clear(&tree.managers);
453 + }
454 +
455 + for(word = 0; word < words ;word++) {
456 + char *s = procfile_lineword(ff, line, word);
457 + if (!s || !*s) continue;
458 + if (*s == '#') break;
459 +
460 + // is this the first word? skip it
461 + if(s == name) continue;
462 +
463 + managed_list_add(&tree.managers, s);
464 + }
465 +
466 + // done with managers, proceed to next line
467 + continue;
468 + }
469 +
470 // find a possibly existing target
471 struct target *w = NULL;
472
@@ -252,15 +493,5 @@ int read_apps_groups_conf(const char *path, const char *file) {
493 }
494
495 procfile_close(ff);
255 -
256 - apps_groups_default_target = get_apps_groups_target("p+!o@w#e$i^r&7*5(-i)l-o_", NULL, "other"); // match nothing
257 - if(!apps_groups_default_target)
258 - fatal("Cannot create default target");
259 - apps_groups_default_target->is_other = true;
260 -
261 - // allow the user to override group 'other'
262 - if(apps_groups_default_target->target)
263 - apps_groups_default_target = apps_groups_default_target->target;
264 -
496 return 0;
497 }
src/collectors/apps.plugin/apps_users_and_groups.c
+28 -10
@@ -2,6 +2,8 @@
2
3 #include "apps_plugin.h"
4
5 +#if (PROCESSES_HAVE_UID == 1) || (PROCESSES_HAVE_GID == 1)
6 +
7 // ----------------------------------------------------------------------------
8 // read users and groups from files
9
@@ -19,7 +21,8 @@ struct user_or_group_ids {
21 char filename[FILENAME_MAX + 1];
22 };
23
22 -int user_id_compare(void* a, void* b) {
24 +#if (PROCESSES_HAVE_UID == 1)
25 +static int user_id_compare(void* a, void* b) {
26 if(((struct user_or_group_id *)a)->id.uid < ((struct user_or_group_id *)b)->id.uid)
27 return -1;
28
@@ -30,7 +33,7 @@ int user_id_compare(void* a, void* b) {
33 return 0;
34 }
35
33 -struct user_or_group_ids all_user_ids = {
36 +static struct user_or_group_ids all_user_ids = {
37 .type = USER_ID,
38
39 .index = {
@@ -42,8 +45,10 @@ struct user_or_group_ids all_user_ids = {
45
46 .filename = "",
47 };
48 +#endif
49
46 -int group_id_compare(void* a, void* b) {
50 +#if (PROCESSES_HAVE_GID == 1)
51 +static int group_id_compare(void* a, void* b) {
52 if(((struct user_or_group_id *)a)->id.gid < ((struct user_or_group_id *)b)->id.gid)
53 return -1;
54
@@ -54,7 +59,7 @@ int group_id_compare(void* a, void* b) {
59 return 0;
60 }
61
57 -struct user_or_group_ids all_group_ids = {
62 +static struct user_or_group_ids all_group_ids = {
63 .type = GROUP_ID,
64
65 .index = {
@@ -66,9 +71,10 @@ struct user_or_group_ids all_group_ids = {
71
72 .filename = "",
73 };
74 +#endif
75
70 -int file_changed(const struct stat *statbuf __maybe_unused, struct timespec *last_modification_time __maybe_unused) {
71 -#if defined(__APPLE__)
76 +static int file_changed(const struct stat *statbuf __maybe_unused, struct timespec *last_modification_time __maybe_unused) {
77 +#if defined(OS_MACOS) || defined(OS_WINDOWS)
78 return 0;
79 #else
80 if(likely(statbuf->st_mtim.tv_sec == last_modification_time->tv_sec &&
@@ -81,7 +87,7 @@ int file_changed(const struct stat *statbuf __maybe_unused, struct timespec *las
87 #endif
88 }
89
84 -int read_user_or_group_ids(struct user_or_group_ids *ids, struct timespec *last_modification_time) {
90 +static int read_user_or_group_ids(struct user_or_group_ids *ids, struct timespec *last_modification_time) {
91 struct stat statbuf;
92 if(unlikely(stat(ids->filename, &statbuf)))
93 return 1;
@@ -109,10 +115,14 @@ int read_user_or_group_ids(struct user_or_group_ids *ids, struct timespec *last_
115
116 struct user_or_group_id *user_or_group_id = callocz(1, sizeof(struct user_or_group_id));
117
118 +#if (PROCESSES_HAVE_UID == 1)
119 if(ids->type == USER_ID)
120 user_or_group_id->id.uid = (uid_t) str2ull(id_string, NULL);
114 - else
121 +#endif
122 +#if (PROCESSES_HAVE_GID == 1)
123 + if(ids->type == GROUP_ID)
124 user_or_group_id->id.gid = (uid_t) str2ull(id_string, NULL);
125 +#endif
126
127 user_or_group_id->name = strdupz(name);
128 user_or_group_id->updated = 1;
@@ -172,6 +182,7 @@ int read_user_or_group_ids(struct user_or_group_ids *ids, struct timespec *last_
182 return 0;
183 }
184
185 +#if (PROCESSES_HAVE_UID == 1)
186 struct user_or_group_id *user_id_find(struct user_or_group_id *user_id_to_find) {
187 if(*netdata_configured_host_prefix) {
188 static struct timespec last_passwd_modification_time;
@@ -183,7 +194,9 @@ struct user_or_group_id *user_id_find(struct user_or_group_id *user_id_to_find)
194
195 return NULL;
196 }
197 +#endif
198
199 +#if (PROCESSES_HAVE_GID == 1)
200 struct user_or_group_id *group_id_find(struct user_or_group_id *group_id_to_find) {
201 if(*netdata_configured_host_prefix) {
202 static struct timespec last_group_modification_time;
@@ -195,12 +208,17 @@ struct user_or_group_id *group_id_find(struct user_or_group_id *group_id_to_find
208
209 return NULL;
210 }
211 +#endif
212 +#endif
213
199 -void users_and_groups_init(void) {
214 +void apps_users_and_groups_init(void) {
215 +#if (PROCESSES_HAVE_UID == 1)
216 snprintfz(all_user_ids.filename, FILENAME_MAX, "%s/etc/passwd", netdata_configured_host_prefix);
217 debug_log("passwd file: '%s'", all_user_ids.filename);
218 +#endif
219
220 +#if (PROCESSES_HAVE_GID == 1)
221 snprintfz(all_group_ids.filename, FILENAME_MAX, "%s/etc/group", netdata_configured_host_prefix);
222 debug_log("group file: '%s'", all_group_ids.filename);
223 +#endif
224 }
206 -
src/collectors/apps.plugin/busy_threads.c new
+76
@@ -0,0 +1,76 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +/*
4 + * A very simple pthreads program to spawn N busy threads.
5 + * It is just used for validating apps.plugin CPU utilization
6 + * calculations per operating system.
7 + *
8 + * Compile with:
9 + *
10 + * gcc -O2 -ggdb -o busy_threads busy_threads.c -pthread
11 + *
12 + * Run as:
13 + *
14 + * busy_threads 2
15 + *
16 + * The above will create 2 busy threads, each using 1 core in user time.
17 + *
18 + */
19 +
20 +#include <stdio.h>
21 +#include <stdlib.h>
22 +#include <pthread.h>
23 +#include <signal.h>
24 +#include <unistd.h>
25 +
26 +volatile int keep_running = 1;
27 +
28 +void handle_signal(int signal) {
29 + keep_running = 0;
30 +}
31 +
32 +void *busy_loop(void *arg) {
33 + while (keep_running) {
34 + // Busy loop to keep CPU at 100%
35 + }
36 + return NULL;
37 +}
38 +
39 +int main(int argc, char *argv[]) {
40 + if (argc != 2) {
41 + fprintf(stderr, "Usage: %s <number of threads>\n", argv[0]);
42 + exit(EXIT_FAILURE);
43 + }
44 +
45 + int num_threads = atoi(argv[1]);
46 + if (num_threads <= 0) {
47 + fprintf(stderr, "Number of threads must be a positive integer.\n");
48 + exit(EXIT_FAILURE);
49 + }
50 +
51 + // Register the signal handler to gracefully exit on Ctrl-C
52 + signal(SIGINT, handle_signal);
53 +
54 + pthread_t *threads = malloc(sizeof(pthread_t) * num_threads);
55 + if (threads == NULL) {
56 + perror("malloc");
57 + exit(EXIT_FAILURE);
58 + }
59 +
60 + // Create threads
61 + for (int i = 0; i < num_threads; i++) {
62 + if (pthread_create(&threads[i], NULL, busy_loop, NULL) != 0) {
63 + perror("pthread_create");
64 + free(threads);
65 + exit(EXIT_FAILURE);
66 + }
67 + }
68 +
69 + // Wait for threads to finish (they never will unless interrupted)
70 + for (int i = 0; i < num_threads; i++) {
71 + pthread_join(threads[i], NULL);
72 + }
73 +
74 + free(threads);
75 + return 0;
76 +}
src/collectors/windows.plugin/GetSystemUptime.c
+34 -34
@@ -1,34 +1,34 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "windows_plugin.h"
4 -#include "windows-internals.h"
5 -
6 -int do_GetSystemUptime(int update_every, usec_t dt __maybe_unused) {
7 - ULONGLONG uptime = GetTickCount64(); // in milliseconds
8 -
9 - static RRDSET *st = NULL;
10 - static RRDDIM *rd_uptime = NULL;
11 - if (!st) {
12 - st = rrdset_create_localhost(
13 - "system"
14 - , "uptime"
15 - , NULL
16 - , "uptime"
17 - , "system.uptime"
18 - , "System Uptime"
19 - , "seconds"
20 - , PLUGIN_WINDOWS_NAME
21 - , "GetSystemUptime"
22 - , NETDATA_CHART_PRIO_SYSTEM_UPTIME
23 - , update_every
24 - , RRDSET_TYPE_LINE
25 - );
26 -
27 - rd_uptime = rrddim_add(st, "uptime", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
28 - }
29 -
30 - rrddim_set_by_pointer(st, rd_uptime, (collected_number)uptime);
31 - rrdset_done(st);
32 -
33 - return 0;
34 -}
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "windows_plugin.h"
4 +#include "windows-internals.h"
5 +
6 +int do_GetSystemUptime(int update_every, usec_t dt __maybe_unused) {
7 + ULONGLONG uptime = GetTickCount64(); // in milliseconds
8 +
9 + static RRDSET *st = NULL;
10 + static RRDDIM *rd_uptime = NULL;
11 + if (!st) {
12 + st = rrdset_create_localhost(
13 + "system"
14 + , "uptime"
15 + , NULL
16 + , "uptime"
17 + , "system.uptime"
18 + , "System Uptime"
19 + , "seconds"
20 + , PLUGIN_WINDOWS_NAME
21 + , "GetSystemUptime"
22 + , NETDATA_CHART_PRIO_SYSTEM_UPTIME
23 + , update_every
24 + , RRDSET_TYPE_LINE
25 + );
26 +
27 + rd_uptime = rrddim_add(st, "uptime", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
28 + }
29 +
30 + rrddim_set_by_pointer(st, rd_uptime, (collected_number)uptime);
31 + rrdset_done(st);
32 +
33 + return 0;
34 +}
src/collectors/windows.plugin/perflib-memory.c
+219 -219
@@ -1,219 +1,219 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "windows_plugin.h"
4 -#include "windows-internals.h"
5 -
6 -#define _COMMON_PLUGIN_NAME "windows.plugin"
7 -#define _COMMON_PLUGIN_MODULE_NAME "PerflibMemory"
8 -#include "../common-contexts/common-contexts.h"
9 -
10 -struct swap {
11 - RRDSET *operations;
12 - RRDDIM *rd_op_read;
13 - RRDDIM *rd_op_write;
14 -
15 - RRDSET *pages;
16 - RRDDIM *rd_page_read;
17 - RRDDIM *rd_page_write;
18 -
19 - COUNTER_DATA pageReadsTotal;
20 - COUNTER_DATA pageWritesTotal;
21 - COUNTER_DATA pageInputTotal;
22 - COUNTER_DATA pageOutputTotal;
23 -};
24 -
25 -struct system_pool {
26 - RRDSET *pool;
27 - RRDDIM *rd_paged;
28 - RRDDIM *rd_nonpaged;
29 -
30 - COUNTER_DATA pagedData;
31 - COUNTER_DATA nonPagedData;
32 -};
33 -
34 -struct swap localSwap = { 0 };
35 -struct system_pool localPool = { 0 };
36 -
37 -void initialize_swap_keys(struct swap *p) {
38 - // SWAP Operations
39 - p->pageReadsTotal.key = "Page Reads/sec";
40 - p->pageWritesTotal.key = "Page Writes/s";
41 -
42 - // Swap Pages
43 - p->pageInputTotal.key = "Pages Input/sec";
44 - p->pageOutputTotal.key = "Pages Output/s";
45 -}
46 -
47 -void initialize_pool_keys(struct system_pool *p) {
48 - p->pagedData.key = "Pool Paged Bytes";
49 - p->nonPagedData.key = "Pool Nonpaged Bytes";
50 -}
51 -
52 -static void initialize(void) {
53 - initialize_swap_keys(&localSwap);
54 - initialize_pool_keys(&localPool);
55 -}
56 -
57 -static void do_memory_swap(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, int update_every)
58 -{
59 - perflibGetObjectCounter(pDataBlock, pObjectType, &localSwap.pageReadsTotal);
60 - perflibGetObjectCounter(pDataBlock, pObjectType, &localSwap.pageWritesTotal);
61 - perflibGetObjectCounter(pDataBlock, pObjectType, &localSwap.pageInputTotal);
62 - perflibGetObjectCounter(pDataBlock, pObjectType, &localSwap.pageOutputTotal);
63 -
64 - if (!localSwap.operations) {
65 - localSwap.operations = rrdset_create_localhost(
66 - "mem"
67 - , "swap_operations", NULL
68 - , "swap"
69 - , "mem.swap_iops"
70 -
71 - , "Swap Operations"
72 - , "operations/s"
73 - , PLUGIN_WINDOWS_NAME
74 - , "PerflibMemory"
75 - , NETDATA_CHART_PRIO_MEM_SWAPIO
76 - , update_every
77 - , RRDSET_TYPE_STACKED
78 - );
79 -
80 - localSwap.rd_op_read = rrddim_add(localSwap.operations, "read", NULL,
81 - 1, 1, RRD_ALGORITHM_INCREMENTAL);
82 - localSwap.rd_op_write = rrddim_add(localSwap.operations, "write", NULL,
83 - 1, -1, RRD_ALGORITHM_INCREMENTAL);
84 - }
85 -
86 - rrddim_set_by_pointer(localSwap.operations,
87 - localSwap.rd_op_read,
88 - (collected_number)localSwap.pageReadsTotal.current.Data);
89 -
90 - rrddim_set_by_pointer(localSwap.operations,
91 - localSwap.rd_op_write,
92 - (collected_number)localSwap.pageWritesTotal.current.Data);
93 - rrdset_done(localSwap.operations);
94 -
95 - if (!localSwap.pages) {
96 - localSwap.pages = rrdset_create_localhost(
97 - "mem"
98 - , "swap_pages", NULL
99 - , "swap"
100 - , "mem.swap_pages_io"
101 -
102 - , "Swap Pages"
103 - , "pages/s"
104 - , PLUGIN_WINDOWS_NAME
105 - , "PerflibMemory"
106 - , NETDATA_CHART_PRIO_MEM_SWAP_PAGES
107 - , update_every
108 - , RRDSET_TYPE_STACKED
109 - );
110 -
111 - localSwap.rd_page_read = rrddim_add(localSwap.pages, "read", NULL,
112 - 1, 1, RRD_ALGORITHM_INCREMENTAL);
113 - localSwap.rd_page_write = rrddim_add(localSwap.pages, "write", NULL,
114 - 1, -1, RRD_ALGORITHM_INCREMENTAL);
115 - }
116 -
117 - rrddim_set_by_pointer(localSwap.pages,
118 - localSwap.rd_page_read,
119 - (collected_number)localSwap.pageInputTotal.current.Data);
120 -
121 - rrddim_set_by_pointer(localSwap.pages,
122 - localSwap.rd_page_write,
123 - (collected_number)localSwap.pageOutputTotal.current.Data);
124 - rrdset_done(localSwap.pages);
125 -}
126 -
127 -static void do_memory_system_pool(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, int update_every)
128 -{
129 - perflibGetObjectCounter(pDataBlock, pObjectType, &localPool.nonPagedData);
130 - perflibGetObjectCounter(pDataBlock, pObjectType, &localPool.pagedData);
131 -
132 - if (!localPool.pool) {
133 - localPool.pool = rrdset_create_localhost(
134 - "mem"
135 - , "system_pool", NULL
136 - , "mem"
137 - , "mem.system_pool_size"
138 -
139 - , "System Memory Pool"
140 - , "bytes"
141 - , PLUGIN_WINDOWS_NAME
142 - , "PerflibMemory"
143 - , NETDATA_CHART_PRIO_MEM_SYSTEM_POOL
144 - , update_every
145 - , RRDSET_TYPE_STACKED
146 - );
147 -
148 - localPool.rd_paged = rrddim_add(localPool.pool, "paged", NULL,
149 - 1, 1, RRD_ALGORITHM_ABSOLUTE);
150 - localPool.rd_nonpaged = rrddim_add(localPool.pool, "pool-paged", NULL,
151 - 1, 1, RRD_ALGORITHM_ABSOLUTE);
152 - }
153 -
154 - rrddim_set_by_pointer(localPool.pool,
155 - localPool.rd_paged,
156 - (collected_number)localPool.pagedData.current.Data);
157 -
158 - rrddim_set_by_pointer(localPool.pool,
159 - localPool.rd_nonpaged,
160 - (collected_number)localPool.nonPagedData.current.Data);
161 - rrdset_done(localPool.pool);
162 -}
163 -
164 -static bool do_memory(PERF_DATA_BLOCK *pDataBlock, int update_every) {
165 - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Memory");
166 - if (!pObjectType)
167 - return false;
168 -
169 - static COUNTER_DATA pagesPerSec = { .key = "Pages/sec" };
170 - static COUNTER_DATA pageFaultsPerSec = { .key = "Page Faults/sec" };
171 -
172 - if(perflibGetObjectCounter(pDataBlock, pObjectType, &pageFaultsPerSec) &&
173 - perflibGetObjectCounter(pDataBlock, pObjectType, &pagesPerSec)) {
174 - ULONGLONG total = pageFaultsPerSec.current.Data;
175 - ULONGLONG major = pagesPerSec.current.Data;
176 - ULONGLONG minor = (total > major) ? total - major : 0;
177 - common_mem_pgfaults(minor, major, update_every);
178 - }
179 -
180 - static COUNTER_DATA availableBytes = { .key = "Available Bytes" };
181 - static COUNTER_DATA availableKBytes = { .key = "Available KBytes" };
182 - static COUNTER_DATA availableMBytes = { .key = "Available MBytes" };
183 - ULONGLONG available_bytes = 0;
184 -
185 - if(perflibGetObjectCounter(pDataBlock, pObjectType, &availableBytes))
186 - available_bytes = availableBytes.current.Data;
187 - else if(perflibGetObjectCounter(pDataBlock, pObjectType, &availableKBytes))
188 - available_bytes = availableKBytes.current.Data * 1024;
189 - else if(perflibGetObjectCounter(pDataBlock, pObjectType, &availableMBytes))
190 - available_bytes = availableMBytes.current.Data * 1024 * 1024;
191 -
192 - common_mem_available(available_bytes, update_every);
193 -
194 - do_memory_swap(pDataBlock, pObjectType, update_every);
195 -
196 - do_memory_system_pool(pDataBlock, pObjectType, update_every);
197 -
198 - return true;
199 -}
200 -
201 -int do_PerflibMemory(int update_every, usec_t dt __maybe_unused) {
202 - static bool initialized = false;
203 -
204 - if(unlikely(!initialized)) {
205 - initialize();
206 - initialized = true;
207 - }
208 -
209 - DWORD id = RegistryFindIDByName("Memory");
210 - if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
211 - return -1;
212 -
213 - PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
214 - if(!pDataBlock) return -1;
215 -
216 - do_memory(pDataBlock, update_every);
217 -
218 - return 0;
219 -}
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "windows_plugin.h"
4 +#include "windows-internals.h"
5 +
6 +#define _COMMON_PLUGIN_NAME "windows.plugin"
7 +#define _COMMON_PLUGIN_MODULE_NAME "PerflibMemory"
8 +#include "../common-contexts/common-contexts.h"
9 +
10 +struct swap {
11 + RRDSET *operations;
12 + RRDDIM *rd_op_read;
13 + RRDDIM *rd_op_write;
14 +
15 + RRDSET *pages;
16 + RRDDIM *rd_page_read;
17 + RRDDIM *rd_page_write;
18 +
19 + COUNTER_DATA pageReadsTotal;
20 + COUNTER_DATA pageWritesTotal;
21 + COUNTER_DATA pageInputTotal;
22 + COUNTER_DATA pageOutputTotal;
23 +};
24 +
25 +struct system_pool {
26 + RRDSET *pool;
27 + RRDDIM *rd_paged;
28 + RRDDIM *rd_nonpaged;
29 +
30 + COUNTER_DATA pagedData;
31 + COUNTER_DATA nonPagedData;
32 +};
33 +
34 +struct swap localSwap = { 0 };
35 +struct system_pool localPool = { 0 };
36 +
37 +void initialize_swap_keys(struct swap *p) {
38 + // SWAP Operations
39 + p->pageReadsTotal.key = "Page Reads/sec";
40 + p->pageWritesTotal.key = "Page Writes/s";
41 +
42 + // Swap Pages
43 + p->pageInputTotal.key = "Pages Input/sec";
44 + p->pageOutputTotal.key = "Pages Output/s";
45 +}
46 +
47 +void initialize_pool_keys(struct system_pool *p) {
48 + p->pagedData.key = "Pool Paged Bytes";
49 + p->nonPagedData.key = "Pool Nonpaged Bytes";
50 +}
51 +
52 +static void initialize(void) {
53 + initialize_swap_keys(&localSwap);
54 + initialize_pool_keys(&localPool);
55 +}
56 +
57 +static void do_memory_swap(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, int update_every)
58 +{
59 + perflibGetObjectCounter(pDataBlock, pObjectType, &localSwap.pageReadsTotal);
60 + perflibGetObjectCounter(pDataBlock, pObjectType, &localSwap.pageWritesTotal);
61 + perflibGetObjectCounter(pDataBlock, pObjectType, &localSwap.pageInputTotal);
62 + perflibGetObjectCounter(pDataBlock, pObjectType, &localSwap.pageOutputTotal);
63 +
64 + if (!localSwap.operations) {
65 + localSwap.operations = rrdset_create_localhost(
66 + "mem"
67 + , "swap_operations", NULL
68 + , "swap"
69 + , "mem.swap_iops"
70 +
71 + , "Swap Operations"
72 + , "operations/s"
73 + , PLUGIN_WINDOWS_NAME
74 + , "PerflibMemory"
75 + , NETDATA_CHART_PRIO_MEM_SWAPIO
76 + , update_every
77 + , RRDSET_TYPE_STACKED
78 + );
79 +
80 + localSwap.rd_op_read = rrddim_add(localSwap.operations, "read", NULL,
81 + 1, 1, RRD_ALGORITHM_INCREMENTAL);
82 + localSwap.rd_op_write = rrddim_add(localSwap.operations, "write", NULL,
83 + 1, -1, RRD_ALGORITHM_INCREMENTAL);
84 + }
85 +
86 + rrddim_set_by_pointer(localSwap.operations,
87 + localSwap.rd_op_read,
88 + (collected_number)localSwap.pageReadsTotal.current.Data);
89 +
90 + rrddim_set_by_pointer(localSwap.operations,
91 + localSwap.rd_op_write,
92 + (collected_number)localSwap.pageWritesTotal.current.Data);
93 + rrdset_done(localSwap.operations);
94 +
95 + if (!localSwap.pages) {
96 + localSwap.pages = rrdset_create_localhost(
97 + "mem"
98 + , "swap_pages", NULL
99 + , "swap"
100 + , "mem.swap_pages_io"
101 +
102 + , "Swap Pages"
103 + , "pages/s"
104 + , PLUGIN_WINDOWS_NAME
105 + , "PerflibMemory"
106 + , NETDATA_CHART_PRIO_MEM_SWAP_PAGES
107 + , update_every
108 + , RRDSET_TYPE_STACKED
109 + );
110 +
111 + localSwap.rd_page_read = rrddim_add(localSwap.pages, "read", NULL,
112 + 1, 1, RRD_ALGORITHM_INCREMENTAL);
113 + localSwap.rd_page_write = rrddim_add(localSwap.pages, "write", NULL,
114 + 1, -1, RRD_ALGORITHM_INCREMENTAL);
115 + }
116 +
117 + rrddim_set_by_pointer(localSwap.pages,
118 + localSwap.rd_page_read,
119 + (collected_number)localSwap.pageInputTotal.current.Data);
120 +
121 + rrddim_set_by_pointer(localSwap.pages,
122 + localSwap.rd_page_write,
123 + (collected_number)localSwap.pageOutputTotal.current.Data);
124 + rrdset_done(localSwap.pages);
125 +}
126 +
127 +static void do_memory_system_pool(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, int update_every)
128 +{
129 + perflibGetObjectCounter(pDataBlock, pObjectType, &localPool.nonPagedData);
130 + perflibGetObjectCounter(pDataBlock, pObjectType, &localPool.pagedData);
131 +
132 + if (!localPool.pool) {
133 + localPool.pool = rrdset_create_localhost(
134 + "mem"
135 + , "system_pool", NULL
136 + , "mem"
137 + , "mem.system_pool_size"
138 +
139 + , "System Memory Pool"
140 + , "bytes"
141 + , PLUGIN_WINDOWS_NAME
142 + , "PerflibMemory"
143 + , NETDATA_CHART_PRIO_MEM_SYSTEM_POOL
144 + , update_every
145 + , RRDSET_TYPE_STACKED
146 + );
147 +
148 + localPool.rd_paged = rrddim_add(localPool.pool, "paged", NULL,
149 + 1, 1, RRD_ALGORITHM_ABSOLUTE);
150 + localPool.rd_nonpaged = rrddim_add(localPool.pool, "pool-paged", NULL,
151 + 1, 1, RRD_ALGORITHM_ABSOLUTE);
152 + }
153 +
154 + rrddim_set_by_pointer(localPool.pool,
155 + localPool.rd_paged,
156 + (collected_number)localPool.pagedData.current.Data);
157 +
158 + rrddim_set_by_pointer(localPool.pool,
159 + localPool.rd_nonpaged,
160 + (collected_number)localPool.nonPagedData.current.Data);
161 + rrdset_done(localPool.pool);
162 +}
163 +
164 +static bool do_memory(PERF_DATA_BLOCK *pDataBlock, int update_every) {
165 + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Memory");
166 + if (!pObjectType)
167 + return false;
168 +
169 + static COUNTER_DATA pagesPerSec = { .key = "Pages/sec" };
170 + static COUNTER_DATA pageFaultsPerSec = { .key = "Page Faults/sec" };
171 +
172 + if(perflibGetObjectCounter(pDataBlock, pObjectType, &pageFaultsPerSec) &&
173 + perflibGetObjectCounter(pDataBlock, pObjectType, &pagesPerSec)) {
174 + ULONGLONG total = pageFaultsPerSec.current.Data;
175 + ULONGLONG major = pagesPerSec.current.Data;
176 + ULONGLONG minor = (total > major) ? total - major : 0;
177 + common_mem_pgfaults(minor, major, update_every);
178 + }
179 +
180 + static COUNTER_DATA availableBytes = { .key = "Available Bytes" };
181 + static COUNTER_DATA availableKBytes = { .key = "Available KBytes" };
182 + static COUNTER_DATA availableMBytes = { .key = "Available MBytes" };
183 + ULONGLONG available_bytes = 0;
184 +
185 + if(perflibGetObjectCounter(pDataBlock, pObjectType, &availableBytes))
186 + available_bytes = availableBytes.current.Data;
187 + else if(perflibGetObjectCounter(pDataBlock, pObjectType, &availableKBytes))
188 + available_bytes = availableKBytes.current.Data * 1024;
189 + else if(perflibGetObjectCounter(pDataBlock, pObjectType, &availableMBytes))
190 + available_bytes = availableMBytes.current.Data * 1024 * 1024;
191 +
192 + common_mem_available(available_bytes, update_every);
193 +
194 + do_memory_swap(pDataBlock, pObjectType, update_every);
195 +
196 + do_memory_system_pool(pDataBlock, pObjectType, update_every);
197 +
198 + return true;
199 +}
200 +
201 +int do_PerflibMemory(int update_every, usec_t dt __maybe_unused) {
202 + static bool initialized = false;
203 +
204 + if(unlikely(!initialized)) {
205 + initialize();
206 + initialized = true;
207 + }
208 +
209 + DWORD id = RegistryFindIDByName("Memory");
210 + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
211 + return -1;
212 +
213 + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
214 + if(!pDataBlock) return -1;
215 +
216 + do_memory(pDataBlock, update_every);
217 +
218 + return 0;
219 +}
src/collectors/windows.plugin/perflib-network.c
+713 -714
@@ -1,714 +1,713 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "windows_plugin.h"
4 -#include "windows-internals.h"
5 -
6 -
7 -#define ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, counter) \
8 - do { \
9 - if ((p)->packets.counter.key) { \
10 - packets += perflibGetObjectCounter((pDataBlock), (pObjectType), &(p)->packets.counter) ? 1 : 0; \
11 - } \
12 - } while (0)
13 -
14 -#define SET_DIM_IF_KEY_AND_UPDATED(p, field) \
15 - do { \
16 - if ((p)->packets.field.key && (p)->packets.field.updated) { \
17 - rrddim_set_by_pointer( \
18 - (p)->packets.st, (p)->packets.rd_##field, (collected_number)(p)->packets.field.current.Data); \
19 - } \
20 - } while (0)
21 -
22 -#define ADD_RRD_DIM_IF_KEY(packet_field, id, name, multiplier, algorithm) \
23 - do { \
24 - if (p->packets.packet_field.key) \
25 - p->packets.rd_##packet_field = rrddim_add(st, id, name, multiplier, 1, algorithm); \
26 - } while (0)
27 -
28 -// --------------------------------------------------------------------------------------------------------------------
29 -// network protocols
30 -
31 -struct network_protocol {
32 - const char *protocol;
33 -
34 - struct {
35 - COUNTER_DATA received;
36 - COUNTER_DATA sent;
37 - COUNTER_DATA delivered;
38 - COUNTER_DATA forwarded;
39 -
40 - COUNTER_DATA InDiscards;
41 - COUNTER_DATA OutDiscards;
42 - COUNTER_DATA InHdrErrors;
43 - COUNTER_DATA InAddrErrors;
44 - COUNTER_DATA InUnknownProtos;
45 - COUNTER_DATA InTooBigErrors;
46 - COUNTER_DATA InTruncatedPkts;
47 - COUNTER_DATA InNoRoutes;
48 - COUNTER_DATA OutNoRoutes;
49 -
50 - COUNTER_DATA InEchoReps;
51 - COUNTER_DATA OutEchoReps;
52 - COUNTER_DATA InDestUnreachs;
53 - COUNTER_DATA OutDestUnreachs;
54 - COUNTER_DATA InRedirects;
55 - COUNTER_DATA OutRedirects;
56 - COUNTER_DATA InEchos;
57 - COUNTER_DATA OutEchos;
58 - COUNTER_DATA InRouterAdvert;
59 - COUNTER_DATA OutRouterAdvert;
60 - COUNTER_DATA InRouterSelect;
61 - COUNTER_DATA OutRouterSelect;
62 - COUNTER_DATA InTimeExcds;
63 - COUNTER_DATA OutTimeExcds;
64 - COUNTER_DATA InParmProbs;
65 - COUNTER_DATA OutParmProbs;
66 - COUNTER_DATA InTimestamps;
67 - COUNTER_DATA OutTimestamps;
68 - COUNTER_DATA InTimestampReps;
69 - COUNTER_DATA OutTimestampReps;
70 -
71 - RRDSET *st;
72 - RRDDIM *rd_received;
73 - RRDDIM *rd_sent;
74 - RRDDIM *rd_forwarded;
75 - RRDDIM *rd_delivered;
76 -
77 - RRDDIM *rd_InDiscards;
78 - RRDDIM *rd_OutDiscards;
79 - RRDDIM *rd_InHdrErrors;
80 - RRDDIM *rd_InAddrErrors;
81 - RRDDIM *rd_InUnknownProtos;
82 - RRDDIM *rd_InTooBigErrors;
83 - RRDDIM *rd_InTruncatedPkts;
84 - RRDDIM *rd_InNoRoutes;
85 - RRDDIM *rd_OutNoRoutes;
86 -
87 - RRDDIM *rd_InEchoReps;
88 - RRDDIM *rd_OutEchoReps;
89 - RRDDIM *rd_InDestUnreachs;
90 - RRDDIM *rd_OutDestUnreachs;
91 - RRDDIM *rd_InRedirects;
92 - RRDDIM *rd_OutRedirects;
93 - RRDDIM *rd_InEchos;
94 - RRDDIM *rd_OutEchos;
95 - RRDDIM *rd_InRouterAdvert;
96 - RRDDIM *rd_OutRouterAdvert;
97 - RRDDIM *rd_InRouterSelect;
98 - RRDDIM *rd_OutRouterSelect;
99 - RRDDIM *rd_InTimeExcds;
100 - RRDDIM *rd_OutTimeExcds;
101 - RRDDIM *rd_InParmProbs;
102 - RRDDIM *rd_OutParmProbs;
103 - RRDDIM *rd_InTimestamps;
104 - RRDDIM *rd_OutTimestamps;
105 - RRDDIM *rd_InTimestampReps;
106 - RRDDIM *rd_OutTimestampReps;
107 -
108 - const char *type;
109 - const char *id;
110 - const char *family;
111 - const char *context;
112 - const char *title;
113 - long priority;
114 - } packets;
115 -
116 -} networks[] = {
117 - {
118 - .protocol = "IPv4",
119 - .packets = {
120 - .received = { .key = "Datagrams Received/sec" },
121 - .sent = { .key = "Datagrams Sent/sec" },
122 - .delivered = { .key = "Datagrams Received Delivered/sec" },
123 - .forwarded = { .key = "Datagrams Forwarded/sec" },
124 - .type = "ipv4",
125 - .id = "packets",
126 - .family = "packets",
127 - .context = "ipv4.packets",
128 - .title = "IPv4 Packets",
129 - .priority = NETDATA_CHART_PRIO_IPV4_PACKETS,
130 - },
131 - },
132 - {
133 - .protocol = "IPv6",
134 - .packets = {
135 - .received = { .key = "Datagrams Received/sec" },
136 - .sent = { .key = "Datagrams Sent/sec" },
137 - .delivered = { .key = "Datagrams Received Delivered/sec" },
138 - .forwarded = { .key = "Datagrams Forwarded/sec" },
139 - .type = "ipv6",
140 - .id = "packets",
141 - .family = "packets",
142 - .context = "ip6.packets",
143 - .title = "IPv6 Packets",
144 - .priority = NETDATA_CHART_PRIO_IPV6_PACKETS,
145 - },
146 - },
147 - {
148 - .protocol = "TCPv4",
149 - .packets = {
150 - .received = { .key = "Segments Received/sec" },
151 - .sent = { .key = "Segments Sent/sec" },
152 - .type = "ipv4",
153 - .id = "tcppackets",
154 - .family = "tcp",
155 - .context = "ipv4.tcppackets",
156 - .title = "IPv4 TCP Packets",
157 - .priority = NETDATA_CHART_PRIO_IPV4_TCP_PACKETS,
158 - },
159 - },
160 - {
161 - .protocol = "TCPv6",
162 - .packets = {
163 - .received = { .key = "Segments Received/sec" },
164 - .sent = { .key = "Segments Sent/sec" },
165 - .type = "ipv6",
166 - .id = "tcppackets",
167 - .family = "tcp6",
168 - .context = "ipv6.tcppackets",
169 - .title = "IPv6 TCP Packets",
170 - .priority = NETDATA_CHART_PRIO_IPV6_TCP_PACKETS,
171 - },
172 - },
173 - {
174 - .protocol = "UDPv4",
175 - .packets = {
176 - .received = { .key = "Datagrams Received/sec" },
177 - .sent = { .key = "Datagrams Sent/sec" },
178 - .type = "ipv4",
179 - .id = "udppackets",
180 - .family = "udp",
181 - .context = "ipv4.udppackets",
182 - .title = "IPv4 UDP Packets",
183 - .priority = NETDATA_CHART_PRIO_IPV4_UDP_PACKETS,
184 - },
185 - },
186 - {
187 - .protocol = "UDPv6",
188 - .packets = {
189 - .received = { .key = "Datagrams Received/sec" },
190 - .sent = { .key = "Datagrams Sent/sec" },
191 - .type = "ipv6",
192 - .id = "udppackets",
193 - .family = "udp6",
194 - .context = "ipv6.udppackets",
195 - .title = "IPv6 UDP Packets",
196 - .priority = NETDATA_CHART_PRIO_IPV6_UDP_PACKETS,
197 - },
198 - },
199 - {
200 - .protocol = "ICMP",
201 - .packets = {
202 - .received = { .key = "Messages Received/sec" },
203 - .sent = { .key = "Messages Sent/sec" },
204 - .type = "ipv4",
205 - .id = "icmp",
206 - .family = "icmp",
207 - .context = "ipv4.icmp",
208 - .title = "IPv4 ICMP Packets",
209 - .priority = NETDATA_CHART_PRIO_IPV4_ICMP_PACKETS,
210 - },
211 - },
212 - {
213 - .protocol = "ICMPv6",
214 - .packets = {
215 - .received = { .key = "Messages Received/sec" },
216 - .sent = { .key = "Messages Sent/sec" },
217 - .type = "ipv6",
218 - .id = "icmp",
219 - .family = "icmp6",
220 - .context = "ipv6.icmp",
221 - .title = "IPv6 ICMP Packets",
222 - .priority = NETDATA_CHART_PRIO_IPV6_ICMP_PACKETS,
223 - },
224 - },
225 -
226 - {
227 - .protocol = "IPv4",
228 - .packets = {
229 - .InDiscards = { .key = "Datagrams Received Discarded" },
230 - .OutDiscards = { .key = "Datagrams Outbound Discarded" },
231 - .OutNoRoutes = { .key = "Datagrams Outbound No Route" },
232 - .InAddrErrors = { .key = "Datagrams Received Address Errors" },
233 - .InHdrErrors = { .key = "Datagrams Received Header Errors" },
234 - .InUnknownProtos = { .key = "Datagrams Received Unknown Protocol" },
235 - .type = "ipv4",
236 - .id = "errors",
237 - .family = "errors",
238 - .context = "ipv4.errors",
239 - .title = "IPv4 errors",
240 - .priority = NETDATA_CHART_PRIO_IPV4_ERRORS,
241 - },
242 - },
243 - {
244 - .protocol = "IPv6",
245 - .packets = {
246 - .InDiscards = { .key = "Datagrams Received Discarded" },
247 - .OutDiscards = { .key = "Datagrams Outbound Discarded" },
248 - .OutNoRoutes = { .key = "Datagrams Outbound No Route" },
249 - .InAddrErrors = { .key = "Datagrams Received Address Errors" },
250 - .InHdrErrors = { .key = "Datagrams Received Header Errors" },
251 - .InUnknownProtos = { .key = "Datagrams Received Unknown Protocol" },
252 - .type = "ipv6",
253 - .id = "errors",
254 - .family = "errors",
255 - .context = "ipv6.errors",
256 - .title = "IPv6 errors",
257 - .priority = NETDATA_CHART_PRIO_IPV6_ERRORS,
258 - },
259 - },
260 - {
261 - .protocol = "ICMP",
262 - .packets =
263 - {
264 - .InEchoReps = {.key = "Received Echo Reply/sec"},
265 - .OutEchoReps = {.key = "Received Echo Reply/sec"},
266 - .InDestUnreachs = {.key = "Received Dest. Unreachable"},
267 - .OutDestUnreachs = {.key = "Sent Destination Unreachable"},
268 - .InRedirects = {.key = "Received Redirect/sec"},
269 - .OutRedirects = {.key = "Sent Redirect/sec"},
270 - .InEchos = {.key = "Received Echo/sec"},
271 - .OutEchos = {.key = "Sent Echo/sec"},
272 - .InRouterAdvert = {.key = NULL},
273 - .OutRouterAdvert = {.key = NULL},
274 - .InRouterSelect = {.key = NULL},
275 - .OutRouterSelect = {.key = NULL},
276 - .InTimeExcds = {.key = "Received Time Exceeded"},
277 - .OutTimeExcds = {.key = "Sent Time Exceeded"},
278 - .InParmProbs = {.key = "Received Parameter Problem"},
279 - .OutParmProbs = {.key = "Sent Parameter Problem"},
280 - .InTimestamps = {.key = "Received Timestamp/sec"},
281 - .OutTimestamps = {.key = "Sent Timestamp/sec"},
282 - .InTimestampReps = {.key = "Received Timestamp Reply/sec"},
283 - .OutTimestampReps = {.key = "Sent Timestamp Reply/sec"},
284 -
285 - .type = "ipv4",
286 - .id = "icmpmsg",
287 - .family = "icmp",
288 - .context = "ipv4.icmpmsg",
289 - .title = "IPv4 ICMP Packets",
290 - .priority = NETDATA_CHART_PRIO_IPV4_ICMP_MESSAGES,
291 - },
292 - },
293 - {
294 - .protocol = "ICMPv6",
295 - .packets =
296 - {
297 - .InEchoReps = {.key = "Received Echo Reply/sec"},
298 - .OutEchoReps = {.key = "Received Echo Reply/sec"},
299 - .InDestUnreachs = {.key = "Received Dest. Unreachable"},
300 - .OutDestUnreachs = {.key = "Sent Destination Unreachable"},
301 - .InRedirects = {.key = "Received Redirect/sec"},
302 - .OutRedirects = {.key = "Sent Redirect/sec"},
303 - .InEchos = {.key = "Received Echo/sec"},
304 - .OutEchos = {.key = "Sent Echo/sec"},
305 - .InRouterAdvert = {.key = NULL},
306 - .OutRouterAdvert = {.key = NULL},
307 - .InRouterSelect = {.key = NULL},
308 - .OutRouterSelect = {.key = NULL},
309 - .InTimeExcds = {.key = "Received Time Exceeded"},
310 - .OutTimeExcds = {.key = "Sent Time Exceeded"},
311 - .InParmProbs = {.key = "Received Parameter Problem"},
312 - .OutParmProbs = {.key = "Sent Parameter Problem"},
313 - .InTimestamps = {.key = "Received Timestamp/sec"},
314 - .OutTimestamps = {.key = "Sent Timestamp/sec"},
315 - .InTimestampReps = {.key = "Received Timestamp Reply/sec"},
316 - .OutTimestampReps = {.key = "Sent Timestamp Reply/sec"},
317 -
318 - .type = "ipv6",
319 - .id = "icmpmsg",
320 - .family = "icmp",
321 - .context = "ipv6.icmpmsg",
322 - .title = "IPv6 ICMP Packets",
323 - .priority = NETDATA_CHART_PRIO_IPV6_ICMP_MESSAGES,
324 - },
325 - },
326 -
327 - // terminator
328 - {
329 - .protocol = NULL,
330 - }
331 -};
332 -
333 -struct network_protocol tcp46 = {
334 - .packets = {
335 - .type = "ip",
336 - .id = "tcppackets",
337 - .family = "tcp",
338 - .context = "ip.tcppackets",
339 - .title = "TCP Packets",
340 - .priority = NETDATA_CHART_PRIO_IP_TCP_PACKETS,
341 - }
342 -};
343 -
344 -static void protocol_packets_chart_update(struct network_protocol *p, int update_every) {
345 - if(!p->packets.st) {
346 - p->packets.st = rrdset_create_localhost(
347 - p->packets.type
348 - , p->packets.id
349 - , NULL
350 - , p->packets.family
351 - , NULL
352 - , p->packets.title
353 - , "packets/s"
354 - , PLUGIN_WINDOWS_NAME
355 - , "PerflibNetwork"
356 - , p->packets.priority
357 - , update_every
358 - , RRDSET_TYPE_AREA
359 - );
360 -
361 - RRDSET *st = p->packets.st;
362 -
363 - ADD_RRD_DIM_IF_KEY(received, "received", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
364 - ADD_RRD_DIM_IF_KEY(sent, "sent", NULL, -1, RRD_ALGORITHM_INCREMENTAL);
365 - ADD_RRD_DIM_IF_KEY(forwarded, "forwarded", NULL, -1, RRD_ALGORITHM_INCREMENTAL);
366 - ADD_RRD_DIM_IF_KEY(delivered, "delivered", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
367 - ADD_RRD_DIM_IF_KEY(InDiscards, "InDiscards", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
368 - ADD_RRD_DIM_IF_KEY(OutDiscards, "OutDiscards", NULL, -1, RRD_ALGORITHM_INCREMENTAL);
369 - ADD_RRD_DIM_IF_KEY(InHdrErrors, "InHdrErrors", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
370 - ADD_RRD_DIM_IF_KEY(InAddrErrors, "InAddrErrors", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
371 - ADD_RRD_DIM_IF_KEY(InUnknownProtos, "InUnknownProtos", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
372 - ADD_RRD_DIM_IF_KEY(InTooBigErrors, "InTooBigErrors", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
373 - ADD_RRD_DIM_IF_KEY(InTruncatedPkts, "InTruncatedPkts", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
374 - ADD_RRD_DIM_IF_KEY(InNoRoutes, "InNoRoutes", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
375 - ADD_RRD_DIM_IF_KEY(OutNoRoutes, "OutNoRoutes", NULL, -1, RRD_ALGORITHM_INCREMENTAL);
376 - ADD_RRD_DIM_IF_KEY(InEchoReps, "InType0", "InEchoReps", 1, RRD_ALGORITHM_INCREMENTAL);
377 - ADD_RRD_DIM_IF_KEY(OutEchoReps, "OutType0", "OutEchoReps", -1, RRD_ALGORITHM_INCREMENTAL);
378 - ADD_RRD_DIM_IF_KEY(InDestUnreachs, "InType3", "InDestUnreachs", 1, RRD_ALGORITHM_INCREMENTAL);
379 - ADD_RRD_DIM_IF_KEY(OutDestUnreachs, "OutType3", "OutDestUnreachs", -1, RRD_ALGORITHM_INCREMENTAL);
380 - ADD_RRD_DIM_IF_KEY(InRedirects, "InType5", "InRedirects", 1, RRD_ALGORITHM_INCREMENTAL);
381 - ADD_RRD_DIM_IF_KEY(OutRedirects, "OutType5", "OutRedirects", -1, RRD_ALGORITHM_INCREMENTAL);
382 - ADD_RRD_DIM_IF_KEY(InEchos, "InType8", "InEchos", 1, RRD_ALGORITHM_INCREMENTAL);
383 - ADD_RRD_DIM_IF_KEY(OutEchos, "OutType8", "OutEchos", -1, RRD_ALGORITHM_INCREMENTAL);
384 - ADD_RRD_DIM_IF_KEY(InRouterAdvert, "InType9", "InRouterAdvert", 1, RRD_ALGORITHM_INCREMENTAL);
385 - ADD_RRD_DIM_IF_KEY(OutRouterAdvert, "OutType9", "OutRouterAdvert", -1, RRD_ALGORITHM_INCREMENTAL);
386 - ADD_RRD_DIM_IF_KEY(InRouterSelect, "InType10", "InRouterSelect", 1, RRD_ALGORITHM_INCREMENTAL);
387 - ADD_RRD_DIM_IF_KEY(OutRouterSelect, "OutType10", "OutRouterSelect", -1, RRD_ALGORITHM_INCREMENTAL);
388 - ADD_RRD_DIM_IF_KEY(InTimeExcds, "InType11", "InTimeExcds", 1, RRD_ALGORITHM_INCREMENTAL);
389 - ADD_RRD_DIM_IF_KEY(OutTimeExcds, "OutType11", "OutTimeExcds", -1, RRD_ALGORITHM_INCREMENTAL);
390 - ADD_RRD_DIM_IF_KEY(InParmProbs, "InType12", "InParmProbs", 1, RRD_ALGORITHM_INCREMENTAL);
391 - ADD_RRD_DIM_IF_KEY(OutParmProbs, "OutType12", "OutParmProbs", -1, RRD_ALGORITHM_INCREMENTAL);
392 - ADD_RRD_DIM_IF_KEY(InTimestamps, "InType13", "InTimestamps", 1, RRD_ALGORITHM_INCREMENTAL);
393 - ADD_RRD_DIM_IF_KEY(OutTimestamps, "OutType13", "OutTimestamps", -1, RRD_ALGORITHM_INCREMENTAL);
394 - ADD_RRD_DIM_IF_KEY(InTimestampReps, "InType14", "InTimestampReps", 1, RRD_ALGORITHM_INCREMENTAL);
395 - ADD_RRD_DIM_IF_KEY(OutTimestampReps, "OutType14", "OutTimestampReps", -1, RRD_ALGORITHM_INCREMENTAL);
396 -
397 - }
398 -
399 - SET_DIM_IF_KEY_AND_UPDATED(p, received);
400 - SET_DIM_IF_KEY_AND_UPDATED(p, sent);
401 -
402 - SET_DIM_IF_KEY_AND_UPDATED(p, forwarded);
403 - SET_DIM_IF_KEY_AND_UPDATED(p, delivered);
404 - SET_DIM_IF_KEY_AND_UPDATED(p, InDiscards);
405 - SET_DIM_IF_KEY_AND_UPDATED(p, OutDiscards);
406 - SET_DIM_IF_KEY_AND_UPDATED(p, InHdrErrors);
407 - SET_DIM_IF_KEY_AND_UPDATED(p, InAddrErrors);
408 - SET_DIM_IF_KEY_AND_UPDATED(p, InUnknownProtos);
409 - SET_DIM_IF_KEY_AND_UPDATED(p, InTooBigErrors);
410 - SET_DIM_IF_KEY_AND_UPDATED(p, InTruncatedPkts);
411 - SET_DIM_IF_KEY_AND_UPDATED(p, InNoRoutes);
412 - SET_DIM_IF_KEY_AND_UPDATED(p, OutNoRoutes);
413 - SET_DIM_IF_KEY_AND_UPDATED(p, InEchoReps);
414 - SET_DIM_IF_KEY_AND_UPDATED(p, OutEchoReps);
415 - SET_DIM_IF_KEY_AND_UPDATED(p, InDestUnreachs);
416 - SET_DIM_IF_KEY_AND_UPDATED(p, OutDestUnreachs);
417 - SET_DIM_IF_KEY_AND_UPDATED(p, InRedirects);
418 - SET_DIM_IF_KEY_AND_UPDATED(p, OutRedirects);
419 - SET_DIM_IF_KEY_AND_UPDATED(p, InEchos);
420 - SET_DIM_IF_KEY_AND_UPDATED(p, OutEchos);
421 - SET_DIM_IF_KEY_AND_UPDATED(p, InRouterAdvert);
422 - SET_DIM_IF_KEY_AND_UPDATED(p, OutRouterAdvert);
423 - SET_DIM_IF_KEY_AND_UPDATED(p, InRouterSelect);
424 - SET_DIM_IF_KEY_AND_UPDATED(p, OutRouterSelect);
425 - SET_DIM_IF_KEY_AND_UPDATED(p, InTimeExcds);
426 - SET_DIM_IF_KEY_AND_UPDATED(p, OutTimeExcds);
427 - SET_DIM_IF_KEY_AND_UPDATED(p, InParmProbs);
428 - SET_DIM_IF_KEY_AND_UPDATED(p, OutParmProbs);
429 - SET_DIM_IF_KEY_AND_UPDATED(p, InTimestamps);
430 - SET_DIM_IF_KEY_AND_UPDATED(p, OutTimestamps);
431 - SET_DIM_IF_KEY_AND_UPDATED(p, InTimestampReps);
432 - SET_DIM_IF_KEY_AND_UPDATED(p, OutTimestampReps);
433 -
434 - rrdset_done(p->packets.st);
435 -}
436 -
437 -static bool do_network_protocol(PERF_DATA_BLOCK *pDataBlock, int update_every, struct network_protocol *p) {
438 - if(!p || !p->protocol) return false;
439 -
440 - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->protocol);
441 - if(!pObjectType) return false;
442 -
443 - size_t packets = 0;
444 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, received);
445 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, sent);
446 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, delivered);
447 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, forwarded);
448 -
449 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InDiscards);
450 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutDiscards);
451 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InHdrErrors);
452 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InAddrErrors);
453 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InUnknownProtos);
454 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTooBigErrors);
455 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTruncatedPkts);
456 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InNoRoutes);
457 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutNoRoutes);
458 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InEchoReps);
459 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutEchoReps);
460 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InDestUnreachs);
461 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutDestUnreachs);
462 -
463 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InRedirects);
464 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutRedirects);
465 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InEchos);
466 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutEchos);
467 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InRouterAdvert);
468 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutRouterAdvert);
469 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InRouterSelect);
470 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutRouterSelect);
471 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTimeExcds);
472 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutTimeExcds);
473 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InParmProbs);
474 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutParmProbs);
475 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTimestamps);
476 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutTimestamps);
477 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTimestampReps);
478 - ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutTimestampReps);
479 -
480 - if(packets)
481 - protocol_packets_chart_update(p, update_every);
482 -
483 - return true;
484 -}
485 -
486 -// --------------------------------------------------------------------------------------------------------------------
487 -// network interfaces
488 -
489 -struct network_interface {
490 - bool collected_metadata;
491 -
492 - struct {
493 - COUNTER_DATA received;
494 - COUNTER_DATA sent;
495 -
496 - RRDSET *st;
497 - RRDDIM *rd_received;
498 - RRDDIM *rd_sent;
499 - } packets;
500 -
501 - struct {
502 - COUNTER_DATA received;
503 - COUNTER_DATA sent;
504 -
505 - RRDSET *st;
506 - RRDDIM *rd_received;
507 - RRDDIM *rd_sent;
508 - } traffic;
509 -};
510 -
511 -static DICTIONARY *physical_interfaces = NULL, *virtual_interfaces = NULL;
512 -
513 -static void network_interface_init(struct network_interface *ni) {
514 - ni->packets.received.key = "Packets Received/sec";
515 - ni->packets.sent.key = "Packets Sent/sec";
516 -
517 - ni->traffic.received.key = "Bytes Received/sec";
518 - ni->traffic.sent.key = "Bytes Sent/sec";
519 -}
520 -
521 -void dict_interface_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
522 - struct network_interface *ni = value;
523 - network_interface_init(ni);
524 -}
525 -
526 -static void initialize(void) {
527 - physical_interfaces = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
528 - DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct network_interface));
529 -
530 - virtual_interfaces = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
531 - DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct network_interface));
532 -
533 - dictionary_register_insert_callback(physical_interfaces, dict_interface_insert_cb, NULL);
534 - dictionary_register_insert_callback(virtual_interfaces, dict_interface_insert_cb, NULL);
535 -}
536 -
537 -static void add_interface_labels(RRDSET *st, const char *name, bool physical) {
538 - rrdlabels_add(st->rrdlabels, "device", name, RRDLABEL_SRC_AUTO);
539 - rrdlabels_add(st->rrdlabels, "interface_type", physical ? "real" : "virtual", RRDLABEL_SRC_AUTO);
540 -}
541 -
542 -static bool is_physical_interface(const char *name) {
543 - void *d = dictionary_get(physical_interfaces, name);
544 - return d ? true : false;
545 -}
546 -
547 -static bool do_network_interface(PERF_DATA_BLOCK *pDataBlock, int update_every, bool physical) {
548 - DICTIONARY *dict = physical_interfaces;
549 -
550 - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, physical ? "Network Interface" : "Network Adapter");
551 - if(!pObjectType) return false;
552 -
553 - uint64_t total_received = 0, total_sent = 0;
554 -
555 - PERF_INSTANCE_DEFINITION *pi = NULL;
556 - for(LONG i = 0; i < pObjectType->NumInstances ; i++) {
557 - pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
558 - if(!pi) break;
559 -
560 - if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
561 - strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
562 -
563 - if(strcasecmp(windows_shared_buffer, "_Total") == 0)
564 - continue;
565 -
566 - if(!physical && is_physical_interface(windows_shared_buffer))
567 - // this virtual interface is already reported as physical interface
568 - continue;
569 -
570 - struct network_interface *d = dictionary_set(dict, windows_shared_buffer, NULL, sizeof(*d));
571 -
572 - if(!d->collected_metadata) {
573 - // TODO - get metadata about the network interface
574 - d->collected_metadata = true;
575 - }
576 -
577 - if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->traffic.received) &&
578 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->traffic.sent)) {
579 -
580 - if(d->traffic.received.current.Data == 0 && d->traffic.sent.current.Data == 0)
581 - // this interface has not received or sent any traffic
582 - continue;
583 -
584 - if (unlikely(!d->traffic.st)) {
585 - d->traffic.st = rrdset_create_localhost(
586 - "net",
587 - windows_shared_buffer,
588 - NULL,
589 - windows_shared_buffer,
590 - "net.net",
591 - "Bandwidth",
592 - "kilobits/s",
593 - PLUGIN_WINDOWS_NAME,
594 - "PerflibNetwork",
595 - NETDATA_CHART_PRIO_FIRST_NET_IFACE,
596 - update_every,
597 - RRDSET_TYPE_AREA);
598 -
599 - rrdset_flag_set(d->traffic.st, RRDSET_FLAG_DETAIL);
600 -
601 - add_interface_labels(d->traffic.st, windows_shared_buffer, physical);
602 -
603 - d->traffic.rd_received = rrddim_add(d->traffic.st, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL);
604 - d->traffic.rd_sent = rrddim_add(d->traffic.st, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL);
605 - }
606 -
607 - total_received += d->traffic.received.current.Data;
608 - total_sent += d->traffic.sent.current.Data;
609 -
610 - rrddim_set_by_pointer(d->traffic.st, d->traffic.rd_received, (collected_number)d->traffic.received.current.Data);
611 - rrddim_set_by_pointer(d->traffic.st, d->traffic.rd_sent, (collected_number)d->traffic.sent.current.Data);
612 - rrdset_done(d->traffic.st);
613 - }
614 -
615 - if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->packets.received) &&
616 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->packets.sent)) {
617 -
618 - if (unlikely(!d->packets.st)) {
619 - d->packets.st = rrdset_create_localhost(
620 - "net_packets",
621 - windows_shared_buffer,
622 - NULL,
623 - windows_shared_buffer,
624 - "net.packets",
625 - "Packets",
626 - "packets/s",
627 - PLUGIN_WINDOWS_NAME,
628 - "PerflibNetwork",
629 - NETDATA_CHART_PRIO_FIRST_NET_IFACE + 1,
630 - update_every,
631 - RRDSET_TYPE_LINE);
632 -
633 - rrdset_flag_set(d->packets.st, RRDSET_FLAG_DETAIL);
634 -
635 - add_interface_labels(d->traffic.st, windows_shared_buffer, physical);
636 -
637 - d->packets.rd_received = rrddim_add(d->packets.st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
638 - d->packets.rd_sent = rrddim_add(d->packets.st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
639 - }
640 -
641 - rrddim_set_by_pointer(d->packets.st, d->packets.rd_received, (collected_number)d->packets.received.current.Data);
642 - rrddim_set_by_pointer(d->packets.st, d->packets.rd_sent, (collected_number)d->packets.sent.current.Data);
643 - rrdset_done(d->packets.st);
644 - }
645 - }
646 -
647 - if(physical) {
648 - static RRDSET *st = NULL;
649 - static RRDDIM *rd_received = NULL, *rd_sent = NULL;
650 -
651 - if (unlikely(!st)) {
652 - st = rrdset_create_localhost(
653 - "system",
654 - "net",
655 - NULL,
656 - "network",
657 - "system.net",
658 - "Physical Network Interfaces Aggregated Bandwidth",
659 - "kilobits/s",
660 - PLUGIN_WINDOWS_NAME,
661 - "PerflibNetwork",
662 - NETDATA_CHART_PRIO_SYSTEM_NET,
663 - update_every,
664 - RRDSET_TYPE_AREA);
665 -
666 - rd_received = rrddim_add(st, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL);
667 - rd_sent = rrddim_add(st, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL);
668 - }
669 -
670 - rrddim_set_by_pointer(st, rd_received, (collected_number)total_received);
671 - rrddim_set_by_pointer(st, rd_sent, (collected_number)total_sent);
672 - rrdset_done(st);
673 - }
674 -
675 - return true;
676 -}
677 -
678 -int do_PerflibNetwork(int update_every, usec_t dt __maybe_unused) {
679 - static bool initialized = false;
680 -
681 - if(unlikely(!initialized)) {
682 - initialize();
683 - initialized = true;
684 - }
685 -
686 - DWORD id = RegistryFindIDByName("Network Interface");
687 - if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
688 - return -1;
689 -
690 - PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
691 - if(!pDataBlock) return -1;
692 -
693 - do_network_interface(pDataBlock, update_every, true);
694 - do_network_interface(pDataBlock, update_every, false);
695 -
696 - struct network_protocol *tcp4 = NULL, *tcp6 = NULL;
697 - for(size_t i = 0; networks[i].protocol ;i++) {
698 - do_network_protocol(pDataBlock, update_every, &networks[i]);
699 -
700 - if(!tcp4 && strcmp(networks[i].protocol, "TCPv4") == 0)
701 - tcp4 = &networks[i];
702 - if(!tcp6 && strcmp(networks[i].protocol, "TCPv6") == 0)
703 - tcp6 = &networks[i];
704 - }
705 -
706 - if(tcp4 && tcp6) {
707 - tcp46.packets.received = tcp4->packets.received;
708 - tcp46.packets.sent = tcp4->packets.sent;
709 - tcp46.packets.received.current.Data += tcp6->packets.received.current.Data;
710 - tcp46.packets.sent.current.Data += tcp6->packets.sent.current.Data;
711 - protocol_packets_chart_update(&tcp46, update_every);
712 - }
713 - return 0;
714 -}
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "windows_plugin.h"
4 +#include "windows-internals.h"
5 +
6 +#define ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, counter) \
7 + do { \
8 + if ((p)->packets.counter.key) { \
9 + packets += perflibGetObjectCounter((pDataBlock), (pObjectType), &(p)->packets.counter) ? 1 : 0; \
10 + } \
11 + } while (0)
12 +
13 +#define SET_DIM_IF_KEY_AND_UPDATED(p, field) \
14 + do { \
15 + if ((p)->packets.field.key && (p)->packets.field.updated) { \
16 + rrddim_set_by_pointer( \
17 + (p)->packets.st, (p)->packets.rd_##field, (collected_number)(p)->packets.field.current.Data); \
18 + } \
19 + } while (0)
20 +
21 +#define ADD_RRD_DIM_IF_KEY(packet_field, id, name, multiplier, algorithm) \
22 + do { \
23 + if (p->packets.packet_field.key) \
24 + p->packets.rd_##packet_field = rrddim_add(st, id, name, multiplier, 1, algorithm); \
25 + } while (0)
26 +
27 +// --------------------------------------------------------------------------------------------------------------------
28 +// network protocols
29 +
30 +struct network_protocol {
31 + const char *protocol;
32 +
33 + struct {
34 + COUNTER_DATA received;
35 + COUNTER_DATA sent;
36 + COUNTER_DATA delivered;
37 + COUNTER_DATA forwarded;
38 +
39 + COUNTER_DATA InDiscards;
40 + COUNTER_DATA OutDiscards;
41 + COUNTER_DATA InHdrErrors;
42 + COUNTER_DATA InAddrErrors;
43 + COUNTER_DATA InUnknownProtos;
44 + COUNTER_DATA InTooBigErrors;
45 + COUNTER_DATA InTruncatedPkts;
46 + COUNTER_DATA InNoRoutes;
47 + COUNTER_DATA OutNoRoutes;
48 +
49 + COUNTER_DATA InEchoReps;
50 + COUNTER_DATA OutEchoReps;
51 + COUNTER_DATA InDestUnreachs;
52 + COUNTER_DATA OutDestUnreachs;
53 + COUNTER_DATA InRedirects;
54 + COUNTER_DATA OutRedirects;
55 + COUNTER_DATA InEchos;
56 + COUNTER_DATA OutEchos;
57 + COUNTER_DATA InRouterAdvert;
58 + COUNTER_DATA OutRouterAdvert;
59 + COUNTER_DATA InRouterSelect;
60 + COUNTER_DATA OutRouterSelect;
61 + COUNTER_DATA InTimeExcds;
62 + COUNTER_DATA OutTimeExcds;
63 + COUNTER_DATA InParmProbs;
64 + COUNTER_DATA OutParmProbs;
65 + COUNTER_DATA InTimestamps;
66 + COUNTER_DATA OutTimestamps;
67 + COUNTER_DATA InTimestampReps;
68 + COUNTER_DATA OutTimestampReps;
69 +
70 + RRDSET *st;
71 + RRDDIM *rd_received;
72 + RRDDIM *rd_sent;
73 + RRDDIM *rd_forwarded;
74 + RRDDIM *rd_delivered;
75 +
76 + RRDDIM *rd_InDiscards;
77 + RRDDIM *rd_OutDiscards;
78 + RRDDIM *rd_InHdrErrors;
79 + RRDDIM *rd_InAddrErrors;
80 + RRDDIM *rd_InUnknownProtos;
81 + RRDDIM *rd_InTooBigErrors;
82 + RRDDIM *rd_InTruncatedPkts;
83 + RRDDIM *rd_InNoRoutes;
84 + RRDDIM *rd_OutNoRoutes;
85 +
86 + RRDDIM *rd_InEchoReps;
87 + RRDDIM *rd_OutEchoReps;
88 + RRDDIM *rd_InDestUnreachs;
89 + RRDDIM *rd_OutDestUnreachs;
90 + RRDDIM *rd_InRedirects;
91 + RRDDIM *rd_OutRedirects;
92 + RRDDIM *rd_InEchos;
93 + RRDDIM *rd_OutEchos;
94 + RRDDIM *rd_InRouterAdvert;
95 + RRDDIM *rd_OutRouterAdvert;
96 + RRDDIM *rd_InRouterSelect;
97 + RRDDIM *rd_OutRouterSelect;
98 + RRDDIM *rd_InTimeExcds;
99 + RRDDIM *rd_OutTimeExcds;
100 + RRDDIM *rd_InParmProbs;
101 + RRDDIM *rd_OutParmProbs;
102 + RRDDIM *rd_InTimestamps;
103 + RRDDIM *rd_OutTimestamps;
104 + RRDDIM *rd_InTimestampReps;
105 + RRDDIM *rd_OutTimestampReps;
106 +
107 + const char *type;
108 + const char *id;
109 + const char *family;
110 + const char *context;
111 + const char *title;
112 + long priority;
113 + } packets;
114 +
115 +} networks[] = {
116 + {
117 + .protocol = "IPv4",
118 + .packets = {
119 + .received = { .key = "Datagrams Received/sec" },
120 + .sent = { .key = "Datagrams Sent/sec" },
121 + .delivered = { .key = "Datagrams Received Delivered/sec" },
122 + .forwarded = { .key = "Datagrams Forwarded/sec" },
123 + .type = "ipv4",
124 + .id = "packets",
125 + .family = "packets",
126 + .context = "ipv4.packets",
127 + .title = "IPv4 Packets",
128 + .priority = NETDATA_CHART_PRIO_IPV4_PACKETS,
129 + },
130 + },
131 + {
132 + .protocol = "IPv6",
133 + .packets = {
134 + .received = { .key = "Datagrams Received/sec" },
135 + .sent = { .key = "Datagrams Sent/sec" },
136 + .delivered = { .key = "Datagrams Received Delivered/sec" },
137 + .forwarded = { .key = "Datagrams Forwarded/sec" },
138 + .type = "ipv6",
139 + .id = "packets",
140 + .family = "packets",
141 + .context = "ip6.packets",
142 + .title = "IPv6 Packets",
143 + .priority = NETDATA_CHART_PRIO_IPV6_PACKETS,
144 + },
145 + },
146 + {
147 + .protocol = "TCPv4",
148 + .packets = {
149 + .received = { .key = "Segments Received/sec" },
150 + .sent = { .key = "Segments Sent/sec" },
151 + .type = "ipv4",
152 + .id = "tcppackets",
153 + .family = "tcp",
154 + .context = "ipv4.tcppackets",
155 + .title = "IPv4 TCP Packets",
156 + .priority = NETDATA_CHART_PRIO_IPV4_TCP_PACKETS,
157 + },
158 + },
159 + {
160 + .protocol = "TCPv6",
161 + .packets = {
162 + .received = { .key = "Segments Received/sec" },
163 + .sent = { .key = "Segments Sent/sec" },
164 + .type = "ipv6",
165 + .id = "tcppackets",
166 + .family = "tcp6",
167 + .context = "ipv6.tcppackets",
168 + .title = "IPv6 TCP Packets",
169 + .priority = NETDATA_CHART_PRIO_IPV6_TCP_PACKETS,
170 + },
171 + },
172 + {
173 + .protocol = "UDPv4",
174 + .packets = {
175 + .received = { .key = "Datagrams Received/sec" },
176 + .sent = { .key = "Datagrams Sent/sec" },
177 + .type = "ipv4",
178 + .id = "udppackets",
179 + .family = "udp",
180 + .context = "ipv4.udppackets",
181 + .title = "IPv4 UDP Packets",
182 + .priority = NETDATA_CHART_PRIO_IPV4_UDP_PACKETS,
183 + },
184 + },
185 + {
186 + .protocol = "UDPv6",
187 + .packets = {
188 + .received = { .key = "Datagrams Received/sec" },
189 + .sent = { .key = "Datagrams Sent/sec" },
190 + .type = "ipv6",
191 + .id = "udppackets",
192 + .family = "udp6",
193 + .context = "ipv6.udppackets",
194 + .title = "IPv6 UDP Packets",
195 + .priority = NETDATA_CHART_PRIO_IPV6_UDP_PACKETS,
196 + },
197 + },
198 + {
199 + .protocol = "ICMP",
200 + .packets = {
201 + .received = { .key = "Messages Received/sec" },
202 + .sent = { .key = "Messages Sent/sec" },
203 + .type = "ipv4",
204 + .id = "icmp",
205 + .family = "icmp",
206 + .context = "ipv4.icmp",
207 + .title = "IPv4 ICMP Packets",
208 + .priority = NETDATA_CHART_PRIO_IPV4_ICMP_PACKETS,
209 + },
210 + },
211 + {
212 + .protocol = "ICMPv6",
213 + .packets = {
214 + .received = { .key = "Messages Received/sec" },
215 + .sent = { .key = "Messages Sent/sec" },
216 + .type = "ipv6",
217 + .id = "icmp",
218 + .family = "icmp6",
219 + .context = "ipv6.icmp",
220 + .title = "IPv6 ICMP Packets",
221 + .priority = NETDATA_CHART_PRIO_IPV6_ICMP_PACKETS,
222 + },
223 + },
224 +
225 + {
226 + .protocol = "IPv4",
227 + .packets = {
228 + .InDiscards = { .key = "Datagrams Received Discarded" },
229 + .OutDiscards = { .key = "Datagrams Outbound Discarded" },
230 + .OutNoRoutes = { .key = "Datagrams Outbound No Route" },
231 + .InAddrErrors = { .key = "Datagrams Received Address Errors" },
232 + .InHdrErrors = { .key = "Datagrams Received Header Errors" },
233 + .InUnknownProtos = { .key = "Datagrams Received Unknown Protocol" },
234 + .type = "ipv4",
235 + .id = "errors",
236 + .family = "errors",
237 + .context = "ipv4.errors",
238 + .title = "IPv4 errors",
239 + .priority = NETDATA_CHART_PRIO_IPV4_ERRORS,
240 + },
241 + },
242 + {
243 + .protocol = "IPv6",
244 + .packets = {
245 + .InDiscards = { .key = "Datagrams Received Discarded" },
246 + .OutDiscards = { .key = "Datagrams Outbound Discarded" },
247 + .OutNoRoutes = { .key = "Datagrams Outbound No Route" },
248 + .InAddrErrors = { .key = "Datagrams Received Address Errors" },
249 + .InHdrErrors = { .key = "Datagrams Received Header Errors" },
250 + .InUnknownProtos = { .key = "Datagrams Received Unknown Protocol" },
251 + .type = "ipv6",
252 + .id = "errors",
253 + .family = "errors",
254 + .context = "ipv6.errors",
255 + .title = "IPv6 errors",
256 + .priority = NETDATA_CHART_PRIO_IPV6_ERRORS,
257 + },
258 + },
259 + {
260 + .protocol = "ICMP",
261 + .packets =
262 + {
263 + .InEchoReps = {.key = "Received Echo Reply/sec"},
264 + .OutEchoReps = {.key = "Received Echo Reply/sec"},
265 + .InDestUnreachs = {.key = "Received Dest. Unreachable"},
266 + .OutDestUnreachs = {.key = "Sent Destination Unreachable"},
267 + .InRedirects = {.key = "Received Redirect/sec"},
268 + .OutRedirects = {.key = "Sent Redirect/sec"},
269 + .InEchos = {.key = "Received Echo/sec"},
270 + .OutEchos = {.key = "Sent Echo/sec"},
271 + .InRouterAdvert = {.key = NULL},
272 + .OutRouterAdvert = {.key = NULL},
273 + .InRouterSelect = {.key = NULL},
274 + .OutRouterSelect = {.key = NULL},
275 + .InTimeExcds = {.key = "Received Time Exceeded"},
276 + .OutTimeExcds = {.key = "Sent Time Exceeded"},
277 + .InParmProbs = {.key = "Received Parameter Problem"},
278 + .OutParmProbs = {.key = "Sent Parameter Problem"},
279 + .InTimestamps = {.key = "Received Timestamp/sec"},
280 + .OutTimestamps = {.key = "Sent Timestamp/sec"},
281 + .InTimestampReps = {.key = "Received Timestamp Reply/sec"},
282 + .OutTimestampReps = {.key = "Sent Timestamp Reply/sec"},
283 +
284 + .type = "ipv4",
285 + .id = "icmpmsg",
286 + .family = "icmp",
287 + .context = "ipv4.icmpmsg",
288 + .title = "IPv4 ICMP Packets",
289 + .priority = NETDATA_CHART_PRIO_IPV4_ICMP_MESSAGES,
290 + },
291 + },
292 + {
293 + .protocol = "ICMPv6",
294 + .packets =
295 + {
296 + .InEchoReps = {.key = "Received Echo Reply/sec"},
297 + .OutEchoReps = {.key = "Received Echo Reply/sec"},
298 + .InDestUnreachs = {.key = "Received Dest. Unreachable"},
299 + .OutDestUnreachs = {.key = "Sent Destination Unreachable"},
300 + .InRedirects = {.key = "Received Redirect/sec"},
301 + .OutRedirects = {.key = "Sent Redirect/sec"},
302 + .InEchos = {.key = "Received Echo/sec"},
303 + .OutEchos = {.key = "Sent Echo/sec"},
304 + .InRouterAdvert = {.key = NULL},
305 + .OutRouterAdvert = {.key = NULL},
306 + .InRouterSelect = {.key = NULL},
307 + .OutRouterSelect = {.key = NULL},
308 + .InTimeExcds = {.key = "Received Time Exceeded"},
309 + .OutTimeExcds = {.key = "Sent Time Exceeded"},
310 + .InParmProbs = {.key = "Received Parameter Problem"},
311 + .OutParmProbs = {.key = "Sent Parameter Problem"},
312 + .InTimestamps = {.key = "Received Timestamp/sec"},
313 + .OutTimestamps = {.key = "Sent Timestamp/sec"},
314 + .InTimestampReps = {.key = "Received Timestamp Reply/sec"},
315 + .OutTimestampReps = {.key = "Sent Timestamp Reply/sec"},
316 +
317 + .type = "ipv6",
318 + .id = "icmpmsg",
319 + .family = "icmp",
320 + .context = "ipv6.icmpmsg",
321 + .title = "IPv6 ICMP Packets",
322 + .priority = NETDATA_CHART_PRIO_IPV6_ICMP_MESSAGES,
323 + },
324 + },
325 +
326 + // terminator
327 + {
328 + .protocol = NULL,
329 + }
330 +};
331 +
332 +struct network_protocol tcp46 = {
333 + .packets = {
334 + .type = "ip",
335 + .id = "tcppackets",
336 + .family = "tcp",
337 + .context = "ip.tcppackets",
338 + .title = "TCP Packets",
339 + .priority = NETDATA_CHART_PRIO_IP_TCP_PACKETS,
340 + }
341 +};
342 +
343 +static void protocol_packets_chart_update(struct network_protocol *p, int update_every) {
344 + if(!p->packets.st) {
345 + p->packets.st = rrdset_create_localhost(
346 + p->packets.type
347 + , p->packets.id
348 + , NULL
349 + , p->packets.family
350 + , NULL
351 + , p->packets.title
352 + , "packets/s"
353 + , PLUGIN_WINDOWS_NAME
354 + , "PerflibNetwork"
355 + , p->packets.priority
356 + , update_every
357 + , RRDSET_TYPE_AREA
358 + );
359 +
360 + RRDSET *st = p->packets.st;
361 +
362 + ADD_RRD_DIM_IF_KEY(received, "received", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
363 + ADD_RRD_DIM_IF_KEY(sent, "sent", NULL, -1, RRD_ALGORITHM_INCREMENTAL);
364 + ADD_RRD_DIM_IF_KEY(forwarded, "forwarded", NULL, -1, RRD_ALGORITHM_INCREMENTAL);
365 + ADD_RRD_DIM_IF_KEY(delivered, "delivered", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
366 + ADD_RRD_DIM_IF_KEY(InDiscards, "InDiscards", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
367 + ADD_RRD_DIM_IF_KEY(OutDiscards, "OutDiscards", NULL, -1, RRD_ALGORITHM_INCREMENTAL);
368 + ADD_RRD_DIM_IF_KEY(InHdrErrors, "InHdrErrors", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
369 + ADD_RRD_DIM_IF_KEY(InAddrErrors, "InAddrErrors", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
370 + ADD_RRD_DIM_IF_KEY(InUnknownProtos, "InUnknownProtos", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
371 + ADD_RRD_DIM_IF_KEY(InTooBigErrors, "InTooBigErrors", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
372 + ADD_RRD_DIM_IF_KEY(InTruncatedPkts, "InTruncatedPkts", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
373 + ADD_RRD_DIM_IF_KEY(InNoRoutes, "InNoRoutes", NULL, 1, RRD_ALGORITHM_INCREMENTAL);
374 + ADD_RRD_DIM_IF_KEY(OutNoRoutes, "OutNoRoutes", NULL, -1, RRD_ALGORITHM_INCREMENTAL);
375 + ADD_RRD_DIM_IF_KEY(InEchoReps, "InType0", "InEchoReps", 1, RRD_ALGORITHM_INCREMENTAL);
376 + ADD_RRD_DIM_IF_KEY(OutEchoReps, "OutType0", "OutEchoReps", -1, RRD_ALGORITHM_INCREMENTAL);
377 + ADD_RRD_DIM_IF_KEY(InDestUnreachs, "InType3", "InDestUnreachs", 1, RRD_ALGORITHM_INCREMENTAL);
378 + ADD_RRD_DIM_IF_KEY(OutDestUnreachs, "OutType3", "OutDestUnreachs", -1, RRD_ALGORITHM_INCREMENTAL);
379 + ADD_RRD_DIM_IF_KEY(InRedirects, "InType5", "InRedirects", 1, RRD_ALGORITHM_INCREMENTAL);
380 + ADD_RRD_DIM_IF_KEY(OutRedirects, "OutType5", "OutRedirects", -1, RRD_ALGORITHM_INCREMENTAL);
381 + ADD_RRD_DIM_IF_KEY(InEchos, "InType8", "InEchos", 1, RRD_ALGORITHM_INCREMENTAL);
382 + ADD_RRD_DIM_IF_KEY(OutEchos, "OutType8", "OutEchos", -1, RRD_ALGORITHM_INCREMENTAL);
383 + ADD_RRD_DIM_IF_KEY(InRouterAdvert, "InType9", "InRouterAdvert", 1, RRD_ALGORITHM_INCREMENTAL);
384 + ADD_RRD_DIM_IF_KEY(OutRouterAdvert, "OutType9", "OutRouterAdvert", -1, RRD_ALGORITHM_INCREMENTAL);
385 + ADD_RRD_DIM_IF_KEY(InRouterSelect, "InType10", "InRouterSelect", 1, RRD_ALGORITHM_INCREMENTAL);
386 + ADD_RRD_DIM_IF_KEY(OutRouterSelect, "OutType10", "OutRouterSelect", -1, RRD_ALGORITHM_INCREMENTAL);
387 + ADD_RRD_DIM_IF_KEY(InTimeExcds, "InType11", "InTimeExcds", 1, RRD_ALGORITHM_INCREMENTAL);
388 + ADD_RRD_DIM_IF_KEY(OutTimeExcds, "OutType11", "OutTimeExcds", -1, RRD_ALGORITHM_INCREMENTAL);
389 + ADD_RRD_DIM_IF_KEY(InParmProbs, "InType12", "InParmProbs", 1, RRD_ALGORITHM_INCREMENTAL);
390 + ADD_RRD_DIM_IF_KEY(OutParmProbs, "OutType12", "OutParmProbs", -1, RRD_ALGORITHM_INCREMENTAL);
391 + ADD_RRD_DIM_IF_KEY(InTimestamps, "InType13", "InTimestamps", 1, RRD_ALGORITHM_INCREMENTAL);
392 + ADD_RRD_DIM_IF_KEY(OutTimestamps, "OutType13", "OutTimestamps", -1, RRD_ALGORITHM_INCREMENTAL);
393 + ADD_RRD_DIM_IF_KEY(InTimestampReps, "InType14", "InTimestampReps", 1, RRD_ALGORITHM_INCREMENTAL);
394 + ADD_RRD_DIM_IF_KEY(OutTimestampReps, "OutType14", "OutTimestampReps", -1, RRD_ALGORITHM_INCREMENTAL);
395 +
396 + }
397 +
398 + SET_DIM_IF_KEY_AND_UPDATED(p, received);
399 + SET_DIM_IF_KEY_AND_UPDATED(p, sent);
400 +
401 + SET_DIM_IF_KEY_AND_UPDATED(p, forwarded);
402 + SET_DIM_IF_KEY_AND_UPDATED(p, delivered);
403 + SET_DIM_IF_KEY_AND_UPDATED(p, InDiscards);
404 + SET_DIM_IF_KEY_AND_UPDATED(p, OutDiscards);
405 + SET_DIM_IF_KEY_AND_UPDATED(p, InHdrErrors);
406 + SET_DIM_IF_KEY_AND_UPDATED(p, InAddrErrors);
407 + SET_DIM_IF_KEY_AND_UPDATED(p, InUnknownProtos);
408 + SET_DIM_IF_KEY_AND_UPDATED(p, InTooBigErrors);
409 + SET_DIM_IF_KEY_AND_UPDATED(p, InTruncatedPkts);
410 + SET_DIM_IF_KEY_AND_UPDATED(p, InNoRoutes);
411 + SET_DIM_IF_KEY_AND_UPDATED(p, OutNoRoutes);
412 + SET_DIM_IF_KEY_AND_UPDATED(p, InEchoReps);
413 + SET_DIM_IF_KEY_AND_UPDATED(p, OutEchoReps);
414 + SET_DIM_IF_KEY_AND_UPDATED(p, InDestUnreachs);
415 + SET_DIM_IF_KEY_AND_UPDATED(p, OutDestUnreachs);
416 + SET_DIM_IF_KEY_AND_UPDATED(p, InRedirects);
417 + SET_DIM_IF_KEY_AND_UPDATED(p, OutRedirects);
418 + SET_DIM_IF_KEY_AND_UPDATED(p, InEchos);
419 + SET_DIM_IF_KEY_AND_UPDATED(p, OutEchos);
420 + SET_DIM_IF_KEY_AND_UPDATED(p, InRouterAdvert);
421 + SET_DIM_IF_KEY_AND_UPDATED(p, OutRouterAdvert);
422 + SET_DIM_IF_KEY_AND_UPDATED(p, InRouterSelect);
423 + SET_DIM_IF_KEY_AND_UPDATED(p, OutRouterSelect);
424 + SET_DIM_IF_KEY_AND_UPDATED(p, InTimeExcds);
425 + SET_DIM_IF_KEY_AND_UPDATED(p, OutTimeExcds);
426 + SET_DIM_IF_KEY_AND_UPDATED(p, InParmProbs);
427 + SET_DIM_IF_KEY_AND_UPDATED(p, OutParmProbs);
428 + SET_DIM_IF_KEY_AND_UPDATED(p, InTimestamps);
429 + SET_DIM_IF_KEY_AND_UPDATED(p, OutTimestamps);
430 + SET_DIM_IF_KEY_AND_UPDATED(p, InTimestampReps);
431 + SET_DIM_IF_KEY_AND_UPDATED(p, OutTimestampReps);
432 +
433 + rrdset_done(p->packets.st);
434 +}
435 +
436 +static bool do_network_protocol(PERF_DATA_BLOCK *pDataBlock, int update_every, struct network_protocol *p) {
437 + if(!p || !p->protocol) return false;
438 +
439 + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->protocol);
440 + if(!pObjectType) return false;
441 +
442 + size_t packets = 0;
443 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, received);
444 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, sent);
445 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, delivered);
446 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, forwarded);
447 +
448 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InDiscards);
449 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutDiscards);
450 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InHdrErrors);
451 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InAddrErrors);
452 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InUnknownProtos);
453 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTooBigErrors);
454 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTruncatedPkts);
455 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InNoRoutes);
456 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutNoRoutes);
457 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InEchoReps);
458 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutEchoReps);
459 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InDestUnreachs);
460 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutDestUnreachs);
461 +
462 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InRedirects);
463 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutRedirects);
464 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InEchos);
465 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutEchos);
466 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InRouterAdvert);
467 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutRouterAdvert);
468 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InRouterSelect);
469 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutRouterSelect);
470 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTimeExcds);
471 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutTimeExcds);
472 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InParmProbs);
473 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutParmProbs);
474 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTimestamps);
475 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutTimestamps);
476 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, InTimestampReps);
477 + ADD_PACKET_IF_KEY(p, packets, pDataBlock, pObjectType, OutTimestampReps);
478 +
479 + if(packets)
480 + protocol_packets_chart_update(p, update_every);
481 +
482 + return true;
483 +}
484 +
485 +// --------------------------------------------------------------------------------------------------------------------
486 +// network interfaces
487 +
488 +struct network_interface {
489 + bool collected_metadata;
490 +
491 + struct {
492 + COUNTER_DATA received;
493 + COUNTER_DATA sent;
494 +
495 + RRDSET *st;
496 + RRDDIM *rd_received;
497 + RRDDIM *rd_sent;
498 + } packets;
499 +
500 + struct {
501 + COUNTER_DATA received;
502 + COUNTER_DATA sent;
503 +
504 + RRDSET *st;
505 + RRDDIM *rd_received;
506 + RRDDIM *rd_sent;
507 + } traffic;
508 +};
509 +
510 +static DICTIONARY *physical_interfaces = NULL, *virtual_interfaces = NULL;
511 +
512 +static void network_interface_init(struct network_interface *ni) {
513 + ni->packets.received.key = "Packets Received/sec";
514 + ni->packets.sent.key = "Packets Sent/sec";
515 +
516 + ni->traffic.received.key = "Bytes Received/sec";
517 + ni->traffic.sent.key = "Bytes Sent/sec";
518 +}
519 +
520 +void dict_interface_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
521 + struct network_interface *ni = value;
522 + network_interface_init(ni);
523 +}
524 +
525 +static void initialize(void) {
526 + physical_interfaces = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
527 + DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct network_interface));
528 +
529 + virtual_interfaces = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
530 + DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct network_interface));
531 +
532 + dictionary_register_insert_callback(physical_interfaces, dict_interface_insert_cb, NULL);
533 + dictionary_register_insert_callback(virtual_interfaces, dict_interface_insert_cb, NULL);
534 +}
535 +
536 +static void add_interface_labels(RRDSET *st, const char *name, bool physical) {
537 + rrdlabels_add(st->rrdlabels, "device", name, RRDLABEL_SRC_AUTO);
538 + rrdlabels_add(st->rrdlabels, "interface_type", physical ? "real" : "virtual", RRDLABEL_SRC_AUTO);
539 +}
540 +
541 +static bool is_physical_interface(const char *name) {
542 + void *d = dictionary_get(physical_interfaces, name);
543 + return d ? true : false;
544 +}
545 +
546 +static bool do_network_interface(PERF_DATA_BLOCK *pDataBlock, int update_every, bool physical) {
547 + DICTIONARY *dict = physical_interfaces;
548 +
549 + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, physical ? "Network Interface" : "Network Adapter");
550 + if(!pObjectType) return false;
551 +
552 + uint64_t total_received = 0, total_sent = 0;
553 +
554 + PERF_INSTANCE_DEFINITION *pi = NULL;
555 + for(LONG i = 0; i < pObjectType->NumInstances ; i++) {
556 + pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
557 + if(!pi) break;
558 +
559 + if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
560 + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
561 +
562 + if(strcasecmp(windows_shared_buffer, "_Total") == 0)
563 + continue;
564 +
565 + if(!physical && is_physical_interface(windows_shared_buffer))
566 + // this virtual interface is already reported as physical interface
567 + continue;
568 +
569 + struct network_interface *d = dictionary_set(dict, windows_shared_buffer, NULL, sizeof(*d));
570 +
571 + if(!d->collected_metadata) {
572 + // TODO - get metadata about the network interface
573 + d->collected_metadata = true;
574 + }
575 +
576 + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->traffic.received) &&
577 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->traffic.sent)) {
578 +
579 + if(d->traffic.received.current.Data == 0 && d->traffic.sent.current.Data == 0)
580 + // this interface has not received or sent any traffic
581 + continue;
582 +
583 + if (unlikely(!d->traffic.st)) {
584 + d->traffic.st = rrdset_create_localhost(
585 + "net",
586 + windows_shared_buffer,
587 + NULL,
588 + windows_shared_buffer,
589 + "net.net",
590 + "Bandwidth",
591 + "kilobits/s",
592 + PLUGIN_WINDOWS_NAME,
593 + "PerflibNetwork",
594 + NETDATA_CHART_PRIO_FIRST_NET_IFACE,
595 + update_every,
596 + RRDSET_TYPE_AREA);
597 +
598 + rrdset_flag_set(d->traffic.st, RRDSET_FLAG_DETAIL);
599 +
600 + add_interface_labels(d->traffic.st, windows_shared_buffer, physical);
601 +
602 + d->traffic.rd_received = rrddim_add(d->traffic.st, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL);
603 + d->traffic.rd_sent = rrddim_add(d->traffic.st, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL);
604 + }
605 +
606 + total_received += d->traffic.received.current.Data;
607 + total_sent += d->traffic.sent.current.Data;
608 +
609 + rrddim_set_by_pointer(d->traffic.st, d->traffic.rd_received, (collected_number)d->traffic.received.current.Data);
610 + rrddim_set_by_pointer(d->traffic.st, d->traffic.rd_sent, (collected_number)d->traffic.sent.current.Data);
611 + rrdset_done(d->traffic.st);
612 + }
613 +
614 + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->packets.received) &&
615 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->packets.sent)) {
616 +
617 + if (unlikely(!d->packets.st)) {
618 + d->packets.st = rrdset_create_localhost(
619 + "net_packets",
620 + windows_shared_buffer,
621 + NULL,
622 + windows_shared_buffer,
623 + "net.packets",
624 + "Packets",
625 + "packets/s",
626 + PLUGIN_WINDOWS_NAME,
627 + "PerflibNetwork",
628 + NETDATA_CHART_PRIO_FIRST_NET_IFACE + 1,
629 + update_every,
630 + RRDSET_TYPE_LINE);
631 +
632 + rrdset_flag_set(d->packets.st, RRDSET_FLAG_DETAIL);
633 +
634 + add_interface_labels(d->traffic.st, windows_shared_buffer, physical);
635 +
636 + d->packets.rd_received = rrddim_add(d->packets.st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
637 + d->packets.rd_sent = rrddim_add(d->packets.st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
638 + }
639 +
640 + rrddim_set_by_pointer(d->packets.st, d->packets.rd_received, (collected_number)d->packets.received.current.Data);
641 + rrddim_set_by_pointer(d->packets.st, d->packets.rd_sent, (collected_number)d->packets.sent.current.Data);
642 + rrdset_done(d->packets.st);
643 + }
644 + }
645 +
646 + if(physical) {
647 + static RRDSET *st = NULL;
648 + static RRDDIM *rd_received = NULL, *rd_sent = NULL;
649 +
650 + if (unlikely(!st)) {
651 + st = rrdset_create_localhost(
652 + "system",
653 + "net",
654 + NULL,
655 + "network",
656 + "system.net",
657 + "Physical Network Interfaces Aggregated Bandwidth",
658 + "kilobits/s",
659 + PLUGIN_WINDOWS_NAME,
660 + "PerflibNetwork",
661 + NETDATA_CHART_PRIO_SYSTEM_NET,
662 + update_every,
663 + RRDSET_TYPE_AREA);
664 +
665 + rd_received = rrddim_add(st, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL);
666 + rd_sent = rrddim_add(st, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL);
667 + }
668 +
669 + rrddim_set_by_pointer(st, rd_received, (collected_number)total_received);
670 + rrddim_set_by_pointer(st, rd_sent, (collected_number)total_sent);
671 + rrdset_done(st);
672 + }
673 +
674 + return true;
675 +}
676 +
677 +int do_PerflibNetwork(int update_every, usec_t dt __maybe_unused) {
678 + static bool initialized = false;
679 +
680 + if(unlikely(!initialized)) {
681 + initialize();
682 + initialized = true;
683 + }
684 +
685 + DWORD id = RegistryFindIDByName("Network Interface");
686 + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
687 + return -1;
688 +
689 + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
690 + if(!pDataBlock) return -1;
691 +
692 + do_network_interface(pDataBlock, update_every, true);
693 + do_network_interface(pDataBlock, update_every, false);
694 +
695 + struct network_protocol *tcp4 = NULL, *tcp6 = NULL;
696 + for(size_t i = 0; networks[i].protocol ;i++) {
697 + do_network_protocol(pDataBlock, update_every, &networks[i]);
698 +
699 + if(!tcp4 && strcmp(networks[i].protocol, "TCPv4") == 0)
700 + tcp4 = &networks[i];
701 + if(!tcp6 && strcmp(networks[i].protocol, "TCPv6") == 0)
702 + tcp6 = &networks[i];
703 + }
704 +
705 + if(tcp4 && tcp6) {
706 + tcp46.packets.received = tcp4->packets.received;
707 + tcp46.packets.sent = tcp4->packets.sent;
708 + tcp46.packets.received.current.Data += tcp6->packets.received.current.Data;
709 + tcp46.packets.sent.current.Data += tcp6->packets.sent.current.Data;
710 + protocol_packets_chart_update(&tcp46, update_every);
711 + }
712 + return 0;
713 +}
src/collectors/windows.plugin/perflib-objects.c
+47 -47
@@ -1,47 +1,47 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "windows_plugin.h"
4 -#include "windows-internals.h"
5 -
6 -#define _COMMON_PLUGIN_NAME "windows.plugin"
7 -#define _COMMON_PLUGIN_MODULE_NAME "PerflibObjects"
8 -#include "../common-contexts/common-contexts.h"
9 -
10 -static void initialize(void) {
11 - ;
12 -}
13 -
14 -static bool do_objects(PERF_DATA_BLOCK *pDataBlock, int update_every) {
15 - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Objects");
16 - if (!pObjectType)
17 - return false;
18 -
19 - static COUNTER_DATA semaphores = { .key = "Semaphores" };
20 -
21 - if(perflibGetObjectCounter(pDataBlock, pObjectType, &semaphores)) {
22 - ULONGLONG sem = semaphores.current.Data;
23 - common_semaphore_ipc(sem, WINDOWS_MAX_KERNEL_OBJECT, _COMMON_PLUGIN_MODULE_NAME, update_every);
24 - }
25 -
26 - return true;
27 -}
28 -
29 -int do_PerflibObjects(int update_every, usec_t dt __maybe_unused) {
30 - static bool initialized = false;
31 -
32 - if(unlikely(!initialized)) {
33 - initialize();
34 - initialized = true;
35 - }
36 -
37 - DWORD id = RegistryFindIDByName("Objects");
38 - if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
39 - return -1;
40 -
41 - PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
42 - if(!pDataBlock) return -1;
43 -
44 - do_objects(pDataBlock, update_every);
45 -
46 - return 0;
47 -}
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "windows_plugin.h"
4 +#include "windows-internals.h"
5 +
6 +#define _COMMON_PLUGIN_NAME "windows.plugin"
7 +#define _COMMON_PLUGIN_MODULE_NAME "PerflibObjects"
8 +#include "../common-contexts/common-contexts.h"
9 +
10 +static void initialize(void) {
11 + ;
12 +}
13 +
14 +static bool do_objects(PERF_DATA_BLOCK *pDataBlock, int update_every) {
15 + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Objects");
16 + if (!pObjectType)
17 + return false;
18 +
19 + static COUNTER_DATA semaphores = { .key = "Semaphores" };
20 +
21 + if(perflibGetObjectCounter(pDataBlock, pObjectType, &semaphores)) {
22 + ULONGLONG sem = semaphores.current.Data;
23 + common_semaphore_ipc(sem, WINDOWS_MAX_KERNEL_OBJECT, _COMMON_PLUGIN_MODULE_NAME, update_every);
24 + }
25 +
26 + return true;
27 +}
28 +
29 +int do_PerflibObjects(int update_every, usec_t dt __maybe_unused) {
30 + static bool initialized = false;
31 +
32 + if(unlikely(!initialized)) {
33 + initialize();
34 + initialized = true;
35 + }
36 +
37 + DWORD id = RegistryFindIDByName("Objects");
38 + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
39 + return -1;
40 +
41 + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
42 + if(!pDataBlock) return -1;
43 +
44 + do_objects(pDataBlock, update_every);
45 +
46 + return 0;
47 +}
src/collectors/windows.plugin/perflib-processes.c
+58 -58
@@ -1,58 +1,58 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "windows_plugin.h"
4 -#include "windows-internals.h"
5 -
6 -#define _COMMON_PLUGIN_NAME "windows.plugin"
7 -#define _COMMON_PLUGIN_MODULE_NAME "PerflibProcesses"
8 -#include "../common-contexts/common-contexts.h"
9 -
10 -static void initialize(void) {
11 - ;
12 -}
13 -
14 -static bool do_processes(PERF_DATA_BLOCK *pDataBlock, int update_every) {
15 - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "System");
16 - if (!pObjectType)
17 - return false;
18 -
19 - static COUNTER_DATA processesRunning = { .key = "Processes" };
20 - static COUNTER_DATA contextSwitchPerSec = { .key = "Context Switches/sec" };
21 - static COUNTER_DATA threads = { .key = "Threads" };
22 -
23 - if(perflibGetObjectCounter(pDataBlock, pObjectType, &processesRunning)) {
24 - ULONGLONG running = processesRunning.current.Data;
25 - common_system_processes(running, update_every);
26 - }
27 -
28 - if(perflibGetObjectCounter(pDataBlock, pObjectType, &contextSwitchPerSec)) {
29 - ULONGLONG contexts = contextSwitchPerSec.current.Data;
30 - common_system_context_switch(contexts, update_every);
31 - }
32 -
33 - if(perflibGetObjectCounter(pDataBlock, pObjectType, &threads)) {
34 - ULONGLONG totalThreads = threads.current.Data;
35 - common_system_threads(totalThreads, update_every);
36 - }
37 - return true;
38 -}
39 -
40 -int do_PerflibProcesses(int update_every, usec_t dt __maybe_unused) {
41 - static bool initialized = false;
42 -
43 - if(unlikely(!initialized)) {
44 - initialize();
45 - initialized = true;
46 - }
47 -
48 - DWORD id = RegistryFindIDByName("System");
49 - if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
50 - return -1;
51 -
52 - PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
53 - if(!pDataBlock) return -1;
54 -
55 - do_processes(pDataBlock, update_every);
56 -
57 - return 0;
58 -}
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "windows_plugin.h"
4 +#include "windows-internals.h"
5 +
6 +#define _COMMON_PLUGIN_NAME "windows.plugin"
7 +#define _COMMON_PLUGIN_MODULE_NAME "PerflibProcesses"
8 +#include "../common-contexts/common-contexts.h"
9 +
10 +static void initialize(void) {
11 + ;
12 +}
13 +
14 +static bool do_processes(PERF_DATA_BLOCK *pDataBlock, int update_every) {
15 + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "System");
16 + if (!pObjectType)
17 + return false;
18 +
19 + static COUNTER_DATA processesRunning = { .key = "Processes" };
20 + static COUNTER_DATA contextSwitchPerSec = { .key = "Context Switches/sec" };
21 + static COUNTER_DATA threads = { .key = "Threads" };
22 +
23 + if(perflibGetObjectCounter(pDataBlock, pObjectType, &processesRunning)) {
24 + ULONGLONG running = processesRunning.current.Data;
25 + common_system_processes(running, update_every);
26 + }
27 +
28 + if(perflibGetObjectCounter(pDataBlock, pObjectType, &contextSwitchPerSec)) {
29 + ULONGLONG contexts = contextSwitchPerSec.current.Data;
30 + common_system_context_switch(contexts, update_every);
31 + }
32 +
33 + if(perflibGetObjectCounter(pDataBlock, pObjectType, &threads)) {
34 + ULONGLONG totalThreads = threads.current.Data;
35 + common_system_threads(totalThreads, update_every);
36 + }
37 + return true;
38 +}
39 +
40 +int do_PerflibProcesses(int update_every, usec_t dt __maybe_unused) {
41 + static bool initialized = false;
42 +
43 + if(unlikely(!initialized)) {
44 + initialize();
45 + initialized = true;
46 + }
47 +
48 + DWORD id = RegistryFindIDByName("System");
49 + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
50 + return -1;
51 +
52 + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
53 + if(!pDataBlock) return -1;
54 +
55 + do_processes(pDataBlock, update_every);
56 +
57 + return 0;
58 +}
src/collectors/windows.plugin/perflib-processor.c
+205 -205
@@ -1,205 +1,205 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "windows_plugin.h"
4 -#include "windows-internals.h"
5 -
6 -#define _COMMON_PLUGIN_NAME "windows.plugin"
7 -#define _COMMON_PLUGIN_MODULE_NAME "PerflibProcesses"
8 -#include "../common-contexts/common-contexts.h"
9 -
10 -struct processor {
11 - bool collected_metadata;
12 -
13 - RRDSET *st;
14 - RRDDIM *rd_user;
15 - RRDDIM *rd_system;
16 - RRDDIM *rd_irq;
17 - RRDDIM *rd_dpc;
18 - RRDDIM *rd_idle;
19 -
20 -// RRDSET *st2;
21 -// RRDDIM *rd2_busy;
22 -
23 - COUNTER_DATA percentProcessorTime;
24 - COUNTER_DATA percentUserTime;
25 - COUNTER_DATA percentPrivilegedTime;
26 - COUNTER_DATA percentDPCTime;
27 - COUNTER_DATA percentInterruptTime;
28 - COUNTER_DATA percentIdleTime;
29 -
30 - COUNTER_DATA interruptsPerSec;
31 -};
32 -
33 -struct processor total = { 0 };
34 -
35 -void initialize_processor_keys(struct processor *p) {
36 - p->percentProcessorTime.key = "% Processor Time";
37 - p->percentUserTime.key = "% User Time";
38 - p->percentPrivilegedTime.key = "% Privileged Time";
39 - p->percentDPCTime.key = "% DPC Time";
40 - p->percentInterruptTime.key = "% Interrupt Time";
41 - p->percentIdleTime.key = "% Idle Time";
42 - p->interruptsPerSec.key = "Interrupts/sec";
43 -}
44 -
45 -void dict_processor_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
46 - struct processor *p = value;
47 - initialize_processor_keys(p);
48 -}
49 -
50 -static DICTIONARY *processors = NULL;
51 -
52 -static void initialize(void) {
53 - initialize_processor_keys(&total);
54 -
55 - processors = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
56 - DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct processor));
57 -
58 - dictionary_register_insert_callback(processors, dict_processor_insert_cb, NULL);
59 -}
60 -
61 -static bool do_processors(PERF_DATA_BLOCK *pDataBlock, int update_every) {
62 - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Processor");
63 - if(!pObjectType) return false;
64 -
65 - static const RRDVAR_ACQUIRED *cpus_var = NULL;
66 - int cores_found = 0;
67 - uint64_t totalIPC = 0;
68 -
69 - PERF_INSTANCE_DEFINITION *pi = NULL;
70 - for(LONG i = 0; i < pObjectType->NumInstances ; i++) {
71 - pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
72 - if(!pi) break;
73 -
74 - if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
75 - strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
76 -
77 - bool is_total = false;
78 - struct processor *p;
79 - int cpu = -1;
80 - if(strcasecmp(windows_shared_buffer, "_Total") == 0) {
81 - p = &total;
82 - is_total = true;
83 - cpu = -1;
84 - }
85 - else {
86 - p = dictionary_set(processors, windows_shared_buffer, NULL, sizeof(*p));
87 - is_total = false;
88 - cpu = str2i(windows_shared_buffer);
89 - snprintfz(windows_shared_buffer, sizeof(windows_shared_buffer), "cpu%d", cpu);
90 -
91 - if(cpu + 1 > cores_found)
92 - cores_found = cpu + 1;
93 - }
94 -
95 - if(!is_total && !p->collected_metadata) {
96 - // TODO collect processor metadata
97 - p->collected_metadata = true;
98 - }
99 -
100 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentProcessorTime);
101 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentUserTime);
102 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentPrivilegedTime);
103 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentDPCTime);
104 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentInterruptTime);
105 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentIdleTime);
106 -
107 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->interruptsPerSec);
108 -
109 - if(!p->st) {
110 - p->st = rrdset_create_localhost(
111 - is_total ? "system" : "cpu"
112 - , is_total ? "cpu" : windows_shared_buffer, NULL
113 - , is_total ? "cpu" : "utilization"
114 - , is_total ? "system.cpu" : "cpu.cpu"
115 - , is_total ? "Total CPU Utilization" : "Core Utilization"
116 - , "percentage"
117 - , PLUGIN_WINDOWS_NAME
118 - , "PerflibProcessor"
119 - , is_total ? NETDATA_CHART_PRIO_SYSTEM_CPU : NETDATA_CHART_PRIO_CPU_PER_CORE
120 - , update_every
121 - , RRDSET_TYPE_STACKED
122 - );
123 -
124 - p->rd_irq = rrddim_add(p->st, "interrupts", "irq", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
125 - p->rd_user = rrddim_add(p->st, "user", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
126 - p->rd_system = rrddim_add(p->st, "privileged", "system", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
127 - p->rd_dpc = rrddim_add(p->st, "dpc", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
128 - p->rd_idle = rrddim_add(p->st, "idle", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
129 - rrddim_hide(p->st, "idle");
130 -
131 - if(!is_total)
132 - rrdlabels_add(p->st->rrdlabels, "cpu", windows_shared_buffer, RRDLABEL_SRC_AUTO);
133 - else
134 - cpus_var = rrdvar_host_variable_add_and_acquire(localhost, "active_processors");
135 - }
136 -
137 - uint64_t user = p->percentUserTime.current.Data;
138 - uint64_t system = p->percentPrivilegedTime.current.Data;
139 - uint64_t dpc = p->percentDPCTime.current.Data;
140 - uint64_t irq = p->percentInterruptTime.current.Data;
141 - uint64_t idle = p->percentIdleTime.current.Data;
142 -
143 - totalIPC += p->interruptsPerSec.current.Data;
144 -
145 - rrddim_set_by_pointer(p->st, p->rd_user, (collected_number)user);
146 - rrddim_set_by_pointer(p->st, p->rd_system, (collected_number)system);
147 - rrddim_set_by_pointer(p->st, p->rd_irq, (collected_number)irq);
148 - rrddim_set_by_pointer(p->st, p->rd_dpc, (collected_number)dpc);
149 - rrddim_set_by_pointer(p->st, p->rd_idle, (collected_number)idle);
150 - rrdset_done(p->st);
151 -
152 -// if(!p->st2) {
153 -// p->st2 = rrdset_create_localhost(
154 -// is_total ? "system" : "cpu2"
155 -// , is_total ? "cpu3" : buffer
156 -// , NULL
157 -// , is_total ? "utilization" : buffer
158 -// , is_total ? "system.cpu3" : "cpu2.cpu"
159 -// , is_total ? "Total CPU Utilization" : "Core Utilization"
160 -// , "percentage"
161 -// , PLUGIN_WINDOWS_NAME
162 -// , "PerflibProcessor"
163 -// , is_total ? NETDATA_CHART_PRIO_SYSTEM_CPU : NETDATA_CHART_PRIO_CPU_PER_CORE
164 -// , update_every
165 -// , RRDSET_TYPE_STACKED
166 -// );
167 -//
168 -// p->rd2_busy = perflib_rrddim_add(p->st2, "busy", NULL, 1, 1, &p->percentProcessorTime);
169 -// rrddim_hide(p->st2, "idle");
170 -//
171 -// if(!is_total)
172 -// rrdlabels_add(p->st->rrdlabels, "cpu", buffer, RRDLABEL_SRC_AUTO);
173 -// }
174 -//
175 -// perflib_rrddim_set_by_pointer(p->st2, p->rd2_busy, &p->percentProcessorTime);
176 -// rrdset_done(p->st2);
177 - }
178 -
179 - if(cpus_var)
180 - rrdvar_host_variable_set(localhost, cpus_var, cores_found);
181 -
182 - common_interrupts(totalIPC, update_every, NULL);
183 -
184 - return true;
185 -}
186 -
187 -int do_PerflibProcessor(int update_every, usec_t dt __maybe_unused) {
188 - static bool initialized = false;
189 -
190 - if(unlikely(!initialized)) {
191 - initialize();
192 - initialized = true;
193 - }
194 -
195 - DWORD id = RegistryFindIDByName("Processor");
196 - if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
197 - return -1;
198 -
199 - PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
200 - if(!pDataBlock) return -1;
201 -
202 - do_processors(pDataBlock, update_every);
203 -
204 - return 0;
205 -}
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "windows_plugin.h"
4 +#include "windows-internals.h"
5 +
6 +#define _COMMON_PLUGIN_NAME "windows.plugin"
7 +#define _COMMON_PLUGIN_MODULE_NAME "PerflibProcesses"
8 +#include "../common-contexts/common-contexts.h"
9 +
10 +struct processor {
11 + bool collected_metadata;
12 +
13 + RRDSET *st;
14 + RRDDIM *rd_user;
15 + RRDDIM *rd_system;
16 + RRDDIM *rd_irq;
17 + RRDDIM *rd_dpc;
18 + RRDDIM *rd_idle;
19 +
20 +// RRDSET *st2;
21 +// RRDDIM *rd2_busy;
22 +
23 + COUNTER_DATA percentProcessorTime;
24 + COUNTER_DATA percentUserTime;
25 + COUNTER_DATA percentPrivilegedTime;
26 + COUNTER_DATA percentDPCTime;
27 + COUNTER_DATA percentInterruptTime;
28 + COUNTER_DATA percentIdleTime;
29 +
30 + COUNTER_DATA interruptsPerSec;
31 +};
32 +
33 +struct processor total = { 0 };
34 +
35 +void initialize_processor_keys(struct processor *p) {
36 + p->percentProcessorTime.key = "% Processor Time";
37 + p->percentUserTime.key = "% User Time";
38 + p->percentPrivilegedTime.key = "% Privileged Time";
39 + p->percentDPCTime.key = "% DPC Time";
40 + p->percentInterruptTime.key = "% Interrupt Time";
41 + p->percentIdleTime.key = "% Idle Time";
42 + p->interruptsPerSec.key = "Interrupts/sec";
43 +}
44 +
45 +void dict_processor_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
46 + struct processor *p = value;
47 + initialize_processor_keys(p);
48 +}
49 +
50 +static DICTIONARY *processors = NULL;
51 +
52 +static void initialize(void) {
53 + initialize_processor_keys(&total);
54 +
55 + processors = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
56 + DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct processor));
57 +
58 + dictionary_register_insert_callback(processors, dict_processor_insert_cb, NULL);
59 +}
60 +
61 +static bool do_processors(PERF_DATA_BLOCK *pDataBlock, int update_every) {
62 + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Processor");
63 + if(!pObjectType) return false;
64 +
65 + static const RRDVAR_ACQUIRED *cpus_var = NULL;
66 + int cores_found = 0;
67 + uint64_t totalIPC = 0;
68 +
69 + PERF_INSTANCE_DEFINITION *pi = NULL;
70 + for(LONG i = 0; i < pObjectType->NumInstances ; i++) {
71 + pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
72 + if(!pi) break;
73 +
74 + if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
75 + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
76 +
77 + bool is_total = false;
78 + struct processor *p;
79 + int cpu = -1;
80 + if(strcasecmp(windows_shared_buffer, "_Total") == 0) {
81 + p = &total;
82 + is_total = true;
83 + cpu = -1;
84 + }
85 + else {
86 + p = dictionary_set(processors, windows_shared_buffer, NULL, sizeof(*p));
87 + is_total = false;
88 + cpu = str2i(windows_shared_buffer);
89 + snprintfz(windows_shared_buffer, sizeof(windows_shared_buffer), "cpu%d", cpu);
90 +
91 + if(cpu + 1 > cores_found)
92 + cores_found = cpu + 1;
93 + }
94 +
95 + if(!is_total && !p->collected_metadata) {
96 + // TODO collect processor metadata
97 + p->collected_metadata = true;
98 + }
99 +
100 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentProcessorTime);
101 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentUserTime);
102 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentPrivilegedTime);
103 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentDPCTime);
104 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentInterruptTime);
105 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->percentIdleTime);
106 +
107 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->interruptsPerSec);
108 +
109 + if(!p->st) {
110 + p->st = rrdset_create_localhost(
111 + is_total ? "system" : "cpu"
112 + , is_total ? "cpu" : windows_shared_buffer, NULL
113 + , is_total ? "cpu" : "utilization"
114 + , is_total ? "system.cpu" : "cpu.cpu"
115 + , is_total ? "Total CPU Utilization" : "Core Utilization"
116 + , "percentage"
117 + , PLUGIN_WINDOWS_NAME
118 + , "PerflibProcessor"
119 + , is_total ? NETDATA_CHART_PRIO_SYSTEM_CPU : NETDATA_CHART_PRIO_CPU_PER_CORE
120 + , update_every
121 + , RRDSET_TYPE_STACKED
122 + );
123 +
124 + p->rd_irq = rrddim_add(p->st, "interrupts", "irq", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
125 + p->rd_user = rrddim_add(p->st, "user", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
126 + p->rd_system = rrddim_add(p->st, "privileged", "system", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
127 + p->rd_dpc = rrddim_add(p->st, "dpc", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
128 + p->rd_idle = rrddim_add(p->st, "idle", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
129 + rrddim_hide(p->st, "idle");
130 +
131 + if(!is_total)
132 + rrdlabels_add(p->st->rrdlabels, "cpu", windows_shared_buffer, RRDLABEL_SRC_AUTO);
133 + else
134 + cpus_var = rrdvar_host_variable_add_and_acquire(localhost, "active_processors");
135 + }
136 +
137 + uint64_t user = p->percentUserTime.current.Data;
138 + uint64_t system = p->percentPrivilegedTime.current.Data;
139 + uint64_t dpc = p->percentDPCTime.current.Data;
140 + uint64_t irq = p->percentInterruptTime.current.Data;
141 + uint64_t idle = p->percentIdleTime.current.Data;
142 +
143 + totalIPC += p->interruptsPerSec.current.Data;
144 +
145 + rrddim_set_by_pointer(p->st, p->rd_user, (collected_number)user);
146 + rrddim_set_by_pointer(p->st, p->rd_system, (collected_number)system);
147 + rrddim_set_by_pointer(p->st, p->rd_irq, (collected_number)irq);
148 + rrddim_set_by_pointer(p->st, p->rd_dpc, (collected_number)dpc);
149 + rrddim_set_by_pointer(p->st, p->rd_idle, (collected_number)idle);
150 + rrdset_done(p->st);
151 +
152 +// if(!p->st2) {
153 +// p->st2 = rrdset_create_localhost(
154 +// is_total ? "system" : "cpu2"
155 +// , is_total ? "cpu3" : buffer
156 +// , NULL
157 +// , is_total ? "utilization" : buffer
158 +// , is_total ? "system.cpu3" : "cpu2.cpu"
159 +// , is_total ? "Total CPU Utilization" : "Core Utilization"
160 +// , "percentage"
161 +// , PLUGIN_WINDOWS_NAME
162 +// , "PerflibProcessor"
163 +// , is_total ? NETDATA_CHART_PRIO_SYSTEM_CPU : NETDATA_CHART_PRIO_CPU_PER_CORE
164 +// , update_every
165 +// , RRDSET_TYPE_STACKED
166 +// );
167 +//
168 +// p->rd2_busy = perflib_rrddim_add(p->st2, "busy", NULL, 1, 1, &p->percentProcessorTime);
169 +// rrddim_hide(p->st2, "idle");
170 +//
171 +// if(!is_total)
172 +// rrdlabels_add(p->st->rrdlabels, "cpu", buffer, RRDLABEL_SRC_AUTO);
173 +// }
174 +//
175 +// perflib_rrddim_set_by_pointer(p->st2, p->rd2_busy, &p->percentProcessorTime);
176 +// rrdset_done(p->st2);
177 + }
178 +
179 + if(cpus_var)
180 + rrdvar_host_variable_set(localhost, cpus_var, cores_found);
181 +
182 + common_interrupts(totalIPC, update_every, NULL);
183 +
184 + return true;
185 +}
186 +
187 +int do_PerflibProcessor(int update_every, usec_t dt __maybe_unused) {
188 + static bool initialized = false;
189 +
190 + if(unlikely(!initialized)) {
191 + initialize();
192 + initialized = true;
193 + }
194 +
195 + DWORD id = RegistryFindIDByName("Processor");
196 + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
197 + return -1;
198 +
199 + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
200 + if(!pDataBlock) return -1;
201 +
202 + do_processors(pDataBlock, update_every);
203 +
204 + return 0;
205 +}
src/collectors/windows.plugin/perflib-rrd.c
+412 -411
@@ -1,411 +1,412 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "perflib-rrd.h"
4 -
5 -#define COLLECTED_NUMBER_PRECISION 10000
6 -
7 -RRDDIM *perflib_rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divider, COUNTER_DATA *cd) {
8 - RRD_ALGORITHM algorithm = RRD_ALGORITHM_ABSOLUTE;
9 -
10 - switch (cd->current.CounterType) {
11 - case PERF_COUNTER_COUNTER:
12 - case PERF_SAMPLE_COUNTER:
13 - case PERF_COUNTER_BULK_COUNT:
14 - // (N1 - N0) / ((D1 - D0) / F)
15 - // multiplier *= cd->current.Frequency / 10000000;
16 - // tested, the frequency is not that useful for netdata
17 - // we get right results without it.
18 - algorithm = RRD_ALGORITHM_INCREMENTAL;
19 - break;
20 -
21 - case PERF_COUNTER_QUEUELEN_TYPE:
22 - case PERF_COUNTER_100NS_QUEUELEN_TYPE:
23 - case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
24 - case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
25 - case PERF_AVERAGE_BULK: // normally not displayed
26 - // (N1 - N0) / (D1 - D0)
27 - algorithm = RRD_ALGORITHM_INCREMENTAL;
28 - break;
29 -
30 - case PERF_OBJ_TIME_TIMER:
31 - case PERF_COUNTER_TIMER:
32 - case PERF_100NSEC_TIMER:
33 - case PERF_PRECISION_SYSTEM_TIMER:
34 - case PERF_PRECISION_100NS_TIMER:
35 - case PERF_PRECISION_OBJECT_TIMER:
36 - case PERF_SAMPLE_FRACTION:
37 - // 100 * (N1 - N0) / (D1 - D0)
38 - multiplier *= 100;
39 - algorithm = RRD_ALGORITHM_INCREMENTAL;
40 - break;
41 -
42 - case PERF_COUNTER_TIMER_INV:
43 - case PERF_100NSEC_TIMER_INV:
44 - // 100 * (1 - ((N1 - N0) / (D1 - D0)))
45 - divider *= COLLECTED_NUMBER_PRECISION;
46 - algorithm = RRD_ALGORITHM_ABSOLUTE;
47 - break;
48 -
49 - case PERF_COUNTER_MULTI_TIMER:
50 - // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1
51 - divider *= COLLECTED_NUMBER_PRECISION;
52 - algorithm = RRD_ALGORITHM_ABSOLUTE;
53 - break;
54 -
55 - case PERF_100NSEC_MULTI_TIMER:
56 - // 100 * ((N1 - N0) / (D1 - D0)) / B1
57 - divider *= COLLECTED_NUMBER_PRECISION;
58 - algorithm = RRD_ALGORITHM_ABSOLUTE;
59 - break;
60 -
61 - case PERF_COUNTER_MULTI_TIMER_INV:
62 - case PERF_100NSEC_MULTI_TIMER_INV:
63 - // 100 * (B1 - ((N1 - N0) / (D1 - D0)))
64 - divider *= COLLECTED_NUMBER_PRECISION;
65 - algorithm = RRD_ALGORITHM_ABSOLUTE;
66 - break;
67 -
68 - case PERF_COUNTER_RAWCOUNT:
69 - case PERF_COUNTER_LARGE_RAWCOUNT:
70 - // N as decimal
71 - algorithm = RRD_ALGORITHM_ABSOLUTE;
72 - break;
73 -
74 - case PERF_COUNTER_RAWCOUNT_HEX:
75 - case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
76 - // N as hexadecimal
77 - algorithm = RRD_ALGORITHM_ABSOLUTE;
78 - break;
79 -
80 - case PERF_COUNTER_DELTA:
81 - case PERF_COUNTER_LARGE_DELTA:
82 - // N1 - N0
83 - algorithm = RRD_ALGORITHM_ABSOLUTE;
84 - break;
85 -
86 - case PERF_RAW_FRACTION:
87 - case PERF_LARGE_RAW_FRACTION:
88 - // 100 * N / B
89 - algorithm = RRD_ALGORITHM_ABSOLUTE;
90 - divider *= COLLECTED_NUMBER_PRECISION;
91 - break;
92 -
93 - case PERF_AVERAGE_TIMER:
94 - // ((N1 - N0) / TB) / (B1 - B0)
95 - // divider *= cd->current.Frequency / 10000000;
96 - algorithm = RRD_ALGORITHM_INCREMENTAL;
97 - break;
98 -
99 - case PERF_ELAPSED_TIME:
100 - // (D0 - N0) / F
101 - algorithm = RRD_ALGORITHM_ABSOLUTE;
102 - break;
103 -
104 - case PERF_COUNTER_TEXT:
105 - case PERF_SAMPLE_BASE:
106 - case PERF_AVERAGE_BASE:
107 - case PERF_COUNTER_MULTI_BASE:
108 - case PERF_RAW_BASE:
109 - case PERF_COUNTER_NODATA:
110 - case PERF_PRECISION_TIMESTAMP:
111 - default:
112 - break;
113 - }
114 -
115 - return rrddim_add(st, id, name, multiplier, divider, algorithm);
116 -}
117 -
118 -#define VALID_DELTA(cd) \
119 - ((cd)->previous.Time > 0 && (cd)->current.Data >= (cd)->previous.Data && (cd)->current.Time > (cd)->previous.Time)
120 -
121 -collected_number perflib_rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, COUNTER_DATA *cd) {
122 - ULONGLONG numerator = 0;
123 - LONGLONG denominator = 0;
124 - double doubleValue = 0.0;
125 - collected_number value;
126 -
127 - switch(cd->current.CounterType) {
128 - case PERF_COUNTER_COUNTER:
129 - case PERF_SAMPLE_COUNTER:
130 - case PERF_COUNTER_BULK_COUNT:
131 - // (N1 - N0) / ((D1 - D0) / F)
132 - value = (collected_number)cd->current.Data;
133 - break;
134 -
135 - case PERF_COUNTER_QUEUELEN_TYPE:
136 - case PERF_COUNTER_100NS_QUEUELEN_TYPE:
137 - case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
138 - case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
139 - case PERF_AVERAGE_BULK: // normally not displayed
140 - // (N1 - N0) / (D1 - D0)
141 - value = (collected_number)cd->current.Data;
142 - break;
143 -
144 - case PERF_OBJ_TIME_TIMER:
145 - case PERF_COUNTER_TIMER:
146 - case PERF_100NSEC_TIMER:
147 - case PERF_PRECISION_SYSTEM_TIMER:
148 - case PERF_PRECISION_100NS_TIMER:
149 - case PERF_PRECISION_OBJECT_TIMER:
150 - case PERF_SAMPLE_FRACTION:
151 - // 100 * (N1 - N0) / (D1 - D0)
152 - value = (collected_number)cd->current.Data;
153 - break;
154 -
155 - case PERF_COUNTER_TIMER_INV:
156 - case PERF_100NSEC_TIMER_INV:
157 - // 100 * (1 - ((N1 - N0) / (D1 - D0)))
158 - if(!VALID_DELTA(cd)) return 0;
159 - numerator = cd->current.Data - cd->previous.Data;
160 - denominator = cd->current.Time - cd->previous.Time;
161 - doubleValue = 100.0 * (1.0 - ((double)numerator / (double)denominator));
162 - // printf("Display value is (timer-inv): %f%%\n", doubleValue);
163 - value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION);
164 - break;
165 -
166 - case PERF_COUNTER_MULTI_TIMER:
167 - // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1
168 - if(!VALID_DELTA(cd)) return 0;
169 - numerator = cd->current.Data - cd->previous.Data;
170 - denominator = cd->current.Time - cd->previous.Time;
171 - denominator /= cd->current.Frequency;
172 - doubleValue = 100.0 * ((double)numerator / (double)denominator) / cd->current.MultiCounterData;
173 - // printf("Display value is (multi-timer): %f%%\n", doubleValue);
174 - value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION);
175 - break;
176 -
177 - case PERF_100NSEC_MULTI_TIMER:
178 - // 100 * ((N1 - N0) / (D1 - D0)) / B1
179 - if(!VALID_DELTA(cd)) return 0;
180 - numerator = cd->current.Data - cd->previous.Data;
181 - denominator = cd->current.Time - cd->previous.Time;
182 - doubleValue = 100.0 * ((double)numerator / (double)denominator) / (double)cd->current.MultiCounterData;
183 - // printf("Display value is (100ns multi-timer): %f%%\n", doubleValue);
184 - value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION);
185 - break;
186 -
187 - case PERF_COUNTER_MULTI_TIMER_INV:
188 - case PERF_100NSEC_MULTI_TIMER_INV:
189 - // 100 * (B1 - ((N1 - N0) / (D1 - D0)))
190 - if(!VALID_DELTA(cd)) return 0;
191 - numerator = cd->current.Data - cd->previous.Data;
192 - denominator = cd->current.Time - cd->previous.Time;
193 - doubleValue = 100.0 * ((double)cd->current.MultiCounterData - ((double)numerator / (double)denominator));
194 - // printf("Display value is (multi-timer-inv): %f%%\n", doubleValue);
195 - value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION);
196 - break;
197 -
198 - case PERF_COUNTER_RAWCOUNT:
199 - case PERF_COUNTER_LARGE_RAWCOUNT:
200 - // N as decimal
201 - value = (collected_number)cd->current.Data;
202 - break;
203 -
204 - case PERF_COUNTER_RAWCOUNT_HEX:
205 - case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
206 - // N as hexadecimal
207 - value = (collected_number)cd->current.Data;
208 - break;
209 -
210 - case PERF_COUNTER_DELTA:
211 - case PERF_COUNTER_LARGE_DELTA:
212 - if(!VALID_DELTA(cd)) return 0;
213 - value = (collected_number)(cd->current.Data - cd->previous.Data);
214 - break;
215 -
216 - case PERF_RAW_FRACTION:
217 - case PERF_LARGE_RAW_FRACTION:
218 - // 100 * N / B
219 - if(!cd->current.Time) return 0;
220 - doubleValue = 100.0 * (double)cd->current.Data / (double)cd->current.Time;
221 - // printf("Display value is (fraction): %f%%\n", doubleValue);
222 - value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION);
223 - break;
224 -
225 - default:
226 - return 0;
227 - }
228 -
229 - return rrddim_set_by_pointer(st, rd, value);
230 -}
231 -
232 -/*
233 -double perflibCalculateValue(RAW_DATA *current, RAW_DATA *previous) {
234 - ULONGLONG numerator = 0;
235 - LONGLONG denominator = 0;
236 - double doubleValue = 0.0;
237 - DWORD dwordValue = 0;
238 -
239 - if (NULL == previous) {
240 - // Return error if the counter type requires two samples to calculate the value.
241 - switch (current->CounterType) {
242 - default:
243 - if (PERF_DELTA_COUNTER != (current->CounterType & PERF_DELTA_COUNTER))
244 - break;
245 - __fallthrough;
246 - // fallthrough
247 -
248 - case PERF_AVERAGE_TIMER: // Special case.
249 - case PERF_AVERAGE_BULK: // Special case.
250 - // printf(" > The counter type requires two samples but only one sample was provided.\n");
251 - return NAN;
252 - }
253 - }
254 - else {
255 - if (current->CounterType != previous->CounterType) {
256 - // printf(" > The samples have inconsistent counter types.\n");
257 - return NAN;
258 - }
259 -
260 - // Check for integer overflow or bad data from provider (the data from
261 - // sample 2 must be greater than the data from sample 1).
262 - if (current->Data < previous->Data)
263 - {
264 - // Can happen for various reasons. Commonly occurs with the Process counterset when
265 - // multiple processes have the same name and one of them starts or stops.
266 - // Normally you'll just drop the older sample and continue.
267 - // printf("> current (%llu) is smaller than previous (%llu).\n", current->Data, previous->Data);
268 - return NAN;
269 - }
270 - }
271 -
272 - switch (current->CounterType) {
273 - case PERF_COUNTER_COUNTER:
274 - case PERF_SAMPLE_COUNTER:
275 - case PERF_COUNTER_BULK_COUNT:
276 - // (N1 - N0) / ((D1 - D0) / F)
277 - numerator = current->Data - previous->Data;
278 - denominator = current->Time - previous->Time;
279 - dwordValue = (DWORD)(numerator / ((double)denominator / current->Frequency));
280 - //printf("Display value is (counter): %lu%s\n", (unsigned long)dwordValue,
281 - // (previous->CounterType == PERF_SAMPLE_COUNTER) ? "" : "/sec");
282 - return (double)dwordValue;
283 -
284 - case PERF_COUNTER_QUEUELEN_TYPE:
285 - case PERF_COUNTER_100NS_QUEUELEN_TYPE:
286 - case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
287 - case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
288 - case PERF_AVERAGE_BULK: // normally not displayed
289 - // (N1 - N0) / (D1 - D0)
290 - numerator = current->Data - previous->Data;
291 - denominator = current->Time - previous->Time;
292 - doubleValue = (double)numerator / denominator;
293 - if (previous->CounterType != PERF_AVERAGE_BULK) {
294 - // printf("Display value is (queuelen): %f\n", doubleValue);
295 - return doubleValue;
296 - }
297 - return NAN;
298 -
299 - case PERF_OBJ_TIME_TIMER:
300 - case PERF_COUNTER_TIMER:
301 - case PERF_100NSEC_TIMER:
302 - case PERF_PRECISION_SYSTEM_TIMER:
303 - case PERF_PRECISION_100NS_TIMER:
304 - case PERF_PRECISION_OBJECT_TIMER:
305 - case PERF_SAMPLE_FRACTION:
306 - // 100 * (N1 - N0) / (D1 - D0)
307 - numerator = current->Data - previous->Data;
308 - denominator = current->Time - previous->Time;
309 - doubleValue = (double)(100 * numerator) / denominator;
310 - // printf("Display value is (timer): %f%%\n", doubleValue);
311 - return doubleValue;
312 -
313 - case PERF_COUNTER_TIMER_INV:
314 - // 100 * (1 - ((N1 - N0) / (D1 - D0)))
315 - numerator = current->Data - previous->Data;
316 - denominator = current->Time - previous->Time;
317 - doubleValue = 100 * (1 - ((double)numerator / denominator));
318 - // printf("Display value is (timer-inv): %f%%\n", doubleValue);
319 - return doubleValue;
320 -
321 - case PERF_100NSEC_TIMER_INV:
322 - // 100 * (1- (N1 - N0) / (D1 - D0))
323 - numerator = current->Data - previous->Data;
324 - denominator = current->Time - previous->Time;
325 - doubleValue = 100 * (1 - (double)numerator / denominator);
326 - // printf("Display value is (100ns-timer-inv): %f%%\n", doubleValue);
327 - return doubleValue;
328 -
329 - case PERF_COUNTER_MULTI_TIMER:
330 - // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1
331 - numerator = current->Data - previous->Data;
332 - denominator = current->Time - previous->Time;
333 - denominator /= current->Frequency;
334 - doubleValue = 100 * ((double)numerator / denominator) / current->MultiCounterData;
335 - // printf("Display value is (multi-timer): %f%%\n", doubleValue);
336 - return doubleValue;
337 -
338 - case PERF_100NSEC_MULTI_TIMER:
339 - // 100 * ((N1 - N0) / (D1 - D0)) / B1
340 - numerator = current->Data - previous->Data;
341 - denominator = current->Time - previous->Time;
342 - doubleValue = 100 * ((double)numerator / (double)denominator) / (double)current->MultiCounterData;
343 - // printf("Display value is (100ns multi-timer): %f%%\n", doubleValue);
344 - return doubleValue;
345 -
346 - case PERF_COUNTER_MULTI_TIMER_INV:
347 - case PERF_100NSEC_MULTI_TIMER_INV:
348 - // 100 * (B1 - ((N1 - N0) / (D1 - D0)))
349 - numerator = current->Data - previous->Data;
350 - denominator = current->Time - previous->Time;
351 - doubleValue = 100.0 * ((double)current->MultiCounterData - ((double)numerator / (double)denominator));
352 - // printf("Display value is (multi-timer-inv): %f%%\n", doubleValue);
353 - return doubleValue;
354 -
355 - case PERF_COUNTER_RAWCOUNT:
356 - case PERF_COUNTER_LARGE_RAWCOUNT:
357 - // N as decimal
358 - // printf("Display value is (rawcount): %llu\n", current->Data);
359 - return (double)current->Data;
360 -
361 - case PERF_COUNTER_RAWCOUNT_HEX:
362 - case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
363 - // N as hexadecimal
364 - // printf("Display value is (hex): 0x%llx\n", current->Data);
365 - return (double)current->Data;
366 -
367 - case PERF_COUNTER_DELTA:
368 - case PERF_COUNTER_LARGE_DELTA:
369 - // N1 - N0
370 - // printf("Display value is (delta): %llu\n", current->Data - previous->Data);
371 - return (double)(current->Data - previous->Data);
372 -
373 - case PERF_RAW_FRACTION:
374 - case PERF_LARGE_RAW_FRACTION:
375 - // 100 * N / B
376 - doubleValue = 100.0 * (double)current->Data / (double)current->Time;
377 - // printf("Display value is (fraction): %f%%\n", doubleValue);
378 - return doubleValue;
379 -
380 - case PERF_AVERAGE_TIMER:
381 - // ((N1 - N0) / TB) / (B1 - B0)
382 - numerator = current->Data - previous->Data;
383 - denominator = current->Time - previous->Time;
384 - doubleValue = (double)numerator / (double)current->Frequency / (double)denominator;
385 - // printf("Display value is (average timer): %f seconds\n", doubleValue);
386 - return doubleValue;
387 -
388 - case PERF_ELAPSED_TIME:
389 - // (D0 - N0) / F
390 - doubleValue = (double)(current->Time - current->Data) / (double)current->Frequency;
391 - // printf("Display value is (elapsed time): %f seconds\n", doubleValue);
392 - return doubleValue;
393 -
394 - case PERF_COUNTER_TEXT:
395 - case PERF_SAMPLE_BASE:
396 - case PERF_AVERAGE_BASE:
397 - case PERF_COUNTER_MULTI_BASE:
398 - case PERF_RAW_BASE:
399 - case PERF_COUNTER_NODATA:
400 - case PERF_PRECISION_TIMESTAMP:
401 - // printf(" > Non-printing counter type: 0x%08x\n", current->CounterType);
402 - return NAN;
403 - break;
404 -
405 - default:
406 - // printf(" > Unrecognized counter type: 0x%08x\n", current->CounterType);
407 - return NAN;
408 - break;
409 - }
410 -}
411 -*/
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "perflib-rrd.h"
4 +#include <windows.h>
5 +
6 +#define COLLECTED_NUMBER_PRECISION 10000
7 +
8 +RRDDIM *perflib_rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divider, COUNTER_DATA *cd) {
9 + RRD_ALGORITHM algorithm = RRD_ALGORITHM_ABSOLUTE;
10 +
11 + switch (cd->current.CounterType) {
12 + case PERF_COUNTER_COUNTER:
13 + case PERF_SAMPLE_COUNTER:
14 + case PERF_COUNTER_BULK_COUNT:
15 + // (N1 - N0) / ((D1 - D0) / F)
16 + // multiplier *= cd->current.Frequency / 10000000;
17 + // tested, the frequency is not that useful for netdata
18 + // we get right results without it.
19 + algorithm = RRD_ALGORITHM_INCREMENTAL;
20 + break;
21 +
22 + case PERF_COUNTER_QUEUELEN_TYPE:
23 + case PERF_COUNTER_100NS_QUEUELEN_TYPE:
24 + case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
25 + case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
26 + case PERF_AVERAGE_BULK: // normally not displayed
27 + // (N1 - N0) / (D1 - D0)
28 + algorithm = RRD_ALGORITHM_INCREMENTAL;
29 + break;
30 +
31 + case PERF_OBJ_TIME_TIMER:
32 + case PERF_COUNTER_TIMER:
33 + case PERF_100NSEC_TIMER:
34 + case PERF_PRECISION_SYSTEM_TIMER:
35 + case PERF_PRECISION_100NS_TIMER:
36 + case PERF_PRECISION_OBJECT_TIMER:
37 + case PERF_SAMPLE_FRACTION:
38 + // 100 * (N1 - N0) / (D1 - D0)
39 + multiplier *= 100;
40 + algorithm = RRD_ALGORITHM_INCREMENTAL;
41 + break;
42 +
43 + case PERF_COUNTER_TIMER_INV:
44 + case PERF_100NSEC_TIMER_INV:
45 + // 100 * (1 - ((N1 - N0) / (D1 - D0)))
46 + divider *= COLLECTED_NUMBER_PRECISION;
47 + algorithm = RRD_ALGORITHM_ABSOLUTE;
48 + break;
49 +
50 + case PERF_COUNTER_MULTI_TIMER:
51 + // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1
52 + divider *= COLLECTED_NUMBER_PRECISION;
53 + algorithm = RRD_ALGORITHM_ABSOLUTE;
54 + break;
55 +
56 + case PERF_100NSEC_MULTI_TIMER:
57 + // 100 * ((N1 - N0) / (D1 - D0)) / B1
58 + divider *= COLLECTED_NUMBER_PRECISION;
59 + algorithm = RRD_ALGORITHM_ABSOLUTE;
60 + break;
61 +
62 + case PERF_COUNTER_MULTI_TIMER_INV:
63 + case PERF_100NSEC_MULTI_TIMER_INV:
64 + // 100 * (B1 - ((N1 - N0) / (D1 - D0)))
65 + divider *= COLLECTED_NUMBER_PRECISION;
66 + algorithm = RRD_ALGORITHM_ABSOLUTE;
67 + break;
68 +
69 + case PERF_COUNTER_RAWCOUNT:
70 + case PERF_COUNTER_LARGE_RAWCOUNT:
71 + // N as decimal
72 + algorithm = RRD_ALGORITHM_ABSOLUTE;
73 + break;
74 +
75 + case PERF_COUNTER_RAWCOUNT_HEX:
76 + case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
77 + // N as hexadecimal
78 + algorithm = RRD_ALGORITHM_ABSOLUTE;
79 + break;
80 +
81 + case PERF_COUNTER_DELTA:
82 + case PERF_COUNTER_LARGE_DELTA:
83 + // N1 - N0
84 + algorithm = RRD_ALGORITHM_ABSOLUTE;
85 + break;
86 +
87 + case PERF_RAW_FRACTION:
88 + case PERF_LARGE_RAW_FRACTION:
89 + // 100 * N / B
90 + algorithm = RRD_ALGORITHM_ABSOLUTE;
91 + divider *= COLLECTED_NUMBER_PRECISION;
92 + break;
93 +
94 + case PERF_AVERAGE_TIMER:
95 + // ((N1 - N0) / TB) / (B1 - B0)
96 + // divider *= cd->current.Frequency / 10000000;
97 + algorithm = RRD_ALGORITHM_INCREMENTAL;
98 + break;
99 +
100 + case PERF_ELAPSED_TIME:
101 + // (D0 - N0) / F
102 + algorithm = RRD_ALGORITHM_ABSOLUTE;
103 + break;
104 +
105 + case PERF_COUNTER_TEXT:
106 + case PERF_SAMPLE_BASE:
107 + case PERF_AVERAGE_BASE:
108 + case PERF_COUNTER_MULTI_BASE:
109 + case PERF_RAW_BASE:
110 + case PERF_COUNTER_NODATA:
111 + case PERF_PRECISION_TIMESTAMP:
112 + default:
113 + break;
114 + }
115 +
116 + return rrddim_add(st, id, name, multiplier, divider, algorithm);
117 +}
118 +
119 +#define VALID_DELTA(cd) \
120 + ((cd)->previous.Time > 0 && (cd)->current.Data >= (cd)->previous.Data && (cd)->current.Time > (cd)->previous.Time)
121 +
122 +collected_number perflib_rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, COUNTER_DATA *cd) {
123 + ULONGLONG numerator = 0;
124 + LONGLONG denominator = 0;
125 + double doubleValue = 0.0;
126 + collected_number value;
127 +
128 + switch(cd->current.CounterType) {
129 + case PERF_COUNTER_COUNTER:
130 + case PERF_SAMPLE_COUNTER:
131 + case PERF_COUNTER_BULK_COUNT:
132 + // (N1 - N0) / ((D1 - D0) / F)
133 + value = (collected_number)cd->current.Data;
134 + break;
135 +
136 + case PERF_COUNTER_QUEUELEN_TYPE:
137 + case PERF_COUNTER_100NS_QUEUELEN_TYPE:
138 + case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
139 + case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
140 + case PERF_AVERAGE_BULK: // normally not displayed
141 + // (N1 - N0) / (D1 - D0)
142 + value = (collected_number)cd->current.Data;
143 + break;
144 +
145 + case PERF_OBJ_TIME_TIMER:
146 + case PERF_COUNTER_TIMER:
147 + case PERF_100NSEC_TIMER:
148 + case PERF_PRECISION_SYSTEM_TIMER:
149 + case PERF_PRECISION_100NS_TIMER:
150 + case PERF_PRECISION_OBJECT_TIMER:
151 + case PERF_SAMPLE_FRACTION:
152 + // 100 * (N1 - N0) / (D1 - D0)
153 + value = (collected_number)cd->current.Data;
154 + break;
155 +
156 + case PERF_COUNTER_TIMER_INV:
157 + case PERF_100NSEC_TIMER_INV:
158 + // 100 * (1 - ((N1 - N0) / (D1 - D0)))
159 + if(!VALID_DELTA(cd)) return 0;
160 + numerator = cd->current.Data - cd->previous.Data;
161 + denominator = cd->current.Time - cd->previous.Time;
162 + doubleValue = 100.0 * (1.0 - ((double)numerator / (double)denominator));
163 + // printf("Display value is (timer-inv): %f%%\n", doubleValue);
164 + value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION);
165 + break;
166 +
167 + case PERF_COUNTER_MULTI_TIMER:
168 + // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1
169 + if(!VALID_DELTA(cd)) return 0;
170 + numerator = cd->current.Data - cd->previous.Data;
171 + denominator = cd->current.Time - cd->previous.Time;
172 + denominator /= cd->current.Frequency;
173 + doubleValue = 100.0 * ((double)numerator / (double)denominator) / cd->current.MultiCounterData;
174 + // printf("Display value is (multi-timer): %f%%\n", doubleValue);
175 + value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION);
176 + break;
177 +
178 + case PERF_100NSEC_MULTI_TIMER:
179 + // 100 * ((N1 - N0) / (D1 - D0)) / B1
180 + if(!VALID_DELTA(cd)) return 0;
181 + numerator = cd->current.Data - cd->previous.Data;
182 + denominator = cd->current.Time - cd->previous.Time;
183 + doubleValue = 100.0 * ((double)numerator / (double)denominator) / (double)cd->current.MultiCounterData;
184 + // printf("Display value is (100ns multi-timer): %f%%\n", doubleValue);
185 + value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION);
186 + break;
187 +
188 + case PERF_COUNTER_MULTI_TIMER_INV:
189 + case PERF_100NSEC_MULTI_TIMER_INV:
190 + // 100 * (B1 - ((N1 - N0) / (D1 - D0)))
191 + if(!VALID_DELTA(cd)) return 0;
192 + numerator = cd->current.Data - cd->previous.Data;
193 + denominator = cd->current.Time - cd->previous.Time;
194 + doubleValue = 100.0 * ((double)cd->current.MultiCounterData - ((double)numerator / (double)denominator));
195 + // printf("Display value is (multi-timer-inv): %f%%\n", doubleValue);
196 + value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION);
197 + break;
198 +
199 + case PERF_COUNTER_RAWCOUNT:
200 + case PERF_COUNTER_LARGE_RAWCOUNT:
201 + // N as decimal
202 + value = (collected_number)cd->current.Data;
203 + break;
204 +
205 + case PERF_COUNTER_RAWCOUNT_HEX:
206 + case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
207 + // N as hexadecimal
208 + value = (collected_number)cd->current.Data;
209 + break;
210 +
211 + case PERF_COUNTER_DELTA:
212 + case PERF_COUNTER_LARGE_DELTA:
213 + if(!VALID_DELTA(cd)) return 0;
214 + value = (collected_number)(cd->current.Data - cd->previous.Data);
215 + break;
216 +
217 + case PERF_RAW_FRACTION:
218 + case PERF_LARGE_RAW_FRACTION:
219 + // 100 * N / B
220 + if(!cd->current.Time) return 0;
221 + doubleValue = 100.0 * (double)cd->current.Data / (double)cd->current.Time;
222 + // printf("Display value is (fraction): %f%%\n", doubleValue);
223 + value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION);
224 + break;
225 +
226 + default:
227 + return 0;
228 + }
229 +
230 + return rrddim_set_by_pointer(st, rd, value);
231 +}
232 +
233 +/*
234 +double perflibCalculateValue(RAW_DATA *current, RAW_DATA *previous) {
235 + ULONGLONG numerator = 0;
236 + LONGLONG denominator = 0;
237 + double doubleValue = 0.0;
238 + DWORD dwordValue = 0;
239 +
240 + if (NULL == previous) {
241 + // Return error if the counter type requires two samples to calculate the value.
242 + switch (current->CounterType) {
243 + default:
244 + if (PERF_DELTA_COUNTER != (current->CounterType & PERF_DELTA_COUNTER))
245 + break;
246 + __fallthrough;
247 + // fallthrough
248 +
249 + case PERF_AVERAGE_TIMER: // Special case.
250 + case PERF_AVERAGE_BULK: // Special case.
251 + // printf(" > The counter type requires two samples but only one sample was provided.\n");
252 + return NAN;
253 + }
254 + }
255 + else {
256 + if (current->CounterType != previous->CounterType) {
257 + // printf(" > The samples have inconsistent counter types.\n");
258 + return NAN;
259 + }
260 +
261 + // Check for integer overflow or bad data from provider (the data from
262 + // sample 2 must be greater than the data from sample 1).
263 + if (current->Data < previous->Data)
264 + {
265 + // Can happen for various reasons. Commonly occurs with the Process counterset when
266 + // multiple processes have the same name and one of them starts or stops.
267 + // Normally you'll just drop the older sample and continue.
268 + // printf("> current (%llu) is smaller than previous (%llu).\n", current->Data, previous->Data);
269 + return NAN;
270 + }
271 + }
272 +
273 + switch (current->CounterType) {
274 + case PERF_COUNTER_COUNTER:
275 + case PERF_SAMPLE_COUNTER:
276 + case PERF_COUNTER_BULK_COUNT:
277 + // (N1 - N0) / ((D1 - D0) / F)
278 + numerator = current->Data - previous->Data;
279 + denominator = current->Time - previous->Time;
280 + dwordValue = (DWORD)(numerator / ((double)denominator / current->Frequency));
281 + //printf("Display value is (counter): %lu%s\n", (unsigned long)dwordValue,
282 + // (previous->CounterType == PERF_SAMPLE_COUNTER) ? "" : "/sec");
283 + return (double)dwordValue;
284 +
285 + case PERF_COUNTER_QUEUELEN_TYPE:
286 + case PERF_COUNTER_100NS_QUEUELEN_TYPE:
287 + case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
288 + case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
289 + case PERF_AVERAGE_BULK: // normally not displayed
290 + // (N1 - N0) / (D1 - D0)
291 + numerator = current->Data - previous->Data;
292 + denominator = current->Time - previous->Time;
293 + doubleValue = (double)numerator / denominator;
294 + if (previous->CounterType != PERF_AVERAGE_BULK) {
295 + // printf("Display value is (queuelen): %f\n", doubleValue);
296 + return doubleValue;
297 + }
298 + return NAN;
299 +
300 + case PERF_OBJ_TIME_TIMER:
301 + case PERF_COUNTER_TIMER:
302 + case PERF_100NSEC_TIMER:
303 + case PERF_PRECISION_SYSTEM_TIMER:
304 + case PERF_PRECISION_100NS_TIMER:
305 + case PERF_PRECISION_OBJECT_TIMER:
306 + case PERF_SAMPLE_FRACTION:
307 + // 100 * (N1 - N0) / (D1 - D0)
308 + numerator = current->Data - previous->Data;
309 + denominator = current->Time - previous->Time;
310 + doubleValue = (double)(100 * numerator) / denominator;
311 + // printf("Display value is (timer): %f%%\n", doubleValue);
312 + return doubleValue;
313 +
314 + case PERF_COUNTER_TIMER_INV:
315 + // 100 * (1 - ((N1 - N0) / (D1 - D0)))
316 + numerator = current->Data - previous->Data;
317 + denominator = current->Time - previous->Time;
318 + doubleValue = 100 * (1 - ((double)numerator / denominator));
319 + // printf("Display value is (timer-inv): %f%%\n", doubleValue);
320 + return doubleValue;
321 +
322 + case PERF_100NSEC_TIMER_INV:
323 + // 100 * (1- (N1 - N0) / (D1 - D0))
324 + numerator = current->Data - previous->Data;
325 + denominator = current->Time - previous->Time;
326 + doubleValue = 100 * (1 - (double)numerator / denominator);
327 + // printf("Display value is (100ns-timer-inv): %f%%\n", doubleValue);
328 + return doubleValue;
329 +
330 + case PERF_COUNTER_MULTI_TIMER:
331 + // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1
332 + numerator = current->Data - previous->Data;
333 + denominator = current->Time - previous->Time;
334 + denominator /= current->Frequency;
335 + doubleValue = 100 * ((double)numerator / denominator) / current->MultiCounterData;
336 + // printf("Display value is (multi-timer): %f%%\n", doubleValue);
337 + return doubleValue;
338 +
339 + case PERF_100NSEC_MULTI_TIMER:
340 + // 100 * ((N1 - N0) / (D1 - D0)) / B1
341 + numerator = current->Data - previous->Data;
342 + denominator = current->Time - previous->Time;
343 + doubleValue = 100 * ((double)numerator / (double)denominator) / (double)current->MultiCounterData;
344 + // printf("Display value is (100ns multi-timer): %f%%\n", doubleValue);
345 + return doubleValue;
346 +
347 + case PERF_COUNTER_MULTI_TIMER_INV:
348 + case PERF_100NSEC_MULTI_TIMER_INV:
349 + // 100 * (B1 - ((N1 - N0) / (D1 - D0)))
350 + numerator = current->Data - previous->Data;
351 + denominator = current->Time - previous->Time;
352 + doubleValue = 100.0 * ((double)current->MultiCounterData - ((double)numerator / (double)denominator));
353 + // printf("Display value is (multi-timer-inv): %f%%\n", doubleValue);
354 + return doubleValue;
355 +
356 + case PERF_COUNTER_RAWCOUNT:
357 + case PERF_COUNTER_LARGE_RAWCOUNT:
358 + // N as decimal
359 + // printf("Display value is (rawcount): %llu\n", current->Data);
360 + return (double)current->Data;
361 +
362 + case PERF_COUNTER_RAWCOUNT_HEX:
363 + case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
364 + // N as hexadecimal
365 + // printf("Display value is (hex): 0x%llx\n", current->Data);
366 + return (double)current->Data;
367 +
368 + case PERF_COUNTER_DELTA:
369 + case PERF_COUNTER_LARGE_DELTA:
370 + // N1 - N0
371 + // printf("Display value is (delta): %llu\n", current->Data - previous->Data);
372 + return (double)(current->Data - previous->Data);
373 +
374 + case PERF_RAW_FRACTION:
375 + case PERF_LARGE_RAW_FRACTION:
376 + // 100 * N / B
377 + doubleValue = 100.0 * (double)current->Data / (double)current->Time;
378 + // printf("Display value is (fraction): %f%%\n", doubleValue);
379 + return doubleValue;
380 +
381 + case PERF_AVERAGE_TIMER:
382 + // ((N1 - N0) / TB) / (B1 - B0)
383 + numerator = current->Data - previous->Data;
384 + denominator = current->Time - previous->Time;
385 + doubleValue = (double)numerator / (double)current->Frequency / (double)denominator;
386 + // printf("Display value is (average timer): %f seconds\n", doubleValue);
387 + return doubleValue;
388 +
389 + case PERF_ELAPSED_TIME:
390 + // (D0 - N0) / F
391 + doubleValue = (double)(current->Time - current->Data) / (double)current->Frequency;
392 + // printf("Display value is (elapsed time): %f seconds\n", doubleValue);
393 + return doubleValue;
394 +
395 + case PERF_COUNTER_TEXT:
396 + case PERF_SAMPLE_BASE:
397 + case PERF_AVERAGE_BASE:
398 + case PERF_COUNTER_MULTI_BASE:
399 + case PERF_RAW_BASE:
400 + case PERF_COUNTER_NODATA:
401 + case PERF_PRECISION_TIMESTAMP:
402 + // printf(" > Non-printing counter type: 0x%08x\n", current->CounterType);
403 + return NAN;
404 + break;
405 +
406 + default:
407 + // printf(" > Unrecognized counter type: 0x%08x\n", current->CounterType);
408 + return NAN;
409 + break;
410 + }
411 +}
412 +*/
src/collectors/windows.plugin/perflib-rrd.h
+11 -12
@@ -1,12 +1,11 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_PERFLIB_RRD_H
4 -#define NETDATA_PERFLIB_RRD_H
5 -
6 -#include "perflib.h"
7 -#include "database/rrd.h"
8 -
9 -RRDDIM *perflib_rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divider, COUNTER_DATA *cd);
10 -collected_number perflib_rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, COUNTER_DATA *cd);
11 -
12 -#endif //NETDATA_PERFLIB_RRD_H
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PERFLIB_RRD_H
4 +#define NETDATA_PERFLIB_RRD_H
5 +
6 +#include "database/rrd.h"
7 +
8 +RRDDIM *perflib_rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divider, COUNTER_DATA *cd);
9 +collected_number perflib_rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, COUNTER_DATA *cd);
10 +
11 +#endif //NETDATA_PERFLIB_RRD_H
src/collectors/windows.plugin/perflib-storage.c
+317 -317
@@ -1,317 +1,317 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "windows_plugin.h"
4 -#include "windows-internals.h"
5 -
6 -#define _COMMON_PLUGIN_NAME PLUGIN_WINDOWS_NAME
7 -#define _COMMON_PLUGIN_MODULE_NAME "PerflibStorage"
8 -#include "../common-contexts/common-contexts.h"
9 -
10 -struct logical_disk {
11 - bool collected_metadata;
12 -
13 - STRING *filesystem;
14 -
15 - RRDSET *st_disk_space;
16 - RRDDIM *rd_disk_space_used;
17 - RRDDIM *rd_disk_space_free;
18 -
19 - COUNTER_DATA percentDiskFree;
20 - // COUNTER_DATA freeMegabytes;
21 -};
22 -
23 -struct physical_disk {
24 - bool collected_metadata;
25 -
26 - STRING *device;
27 - STRING *mount_point;
28 -
29 - ND_DISK_IO disk_io;
30 - COUNTER_DATA diskReadBytesPerSec;
31 - COUNTER_DATA diskWriteBytesPerSec;
32 -
33 - COUNTER_DATA percentIdleTime;
34 - COUNTER_DATA percentDiskTime;
35 - COUNTER_DATA percentDiskReadTime;
36 - COUNTER_DATA percentDiskWriteTime;
37 - COUNTER_DATA currentDiskQueueLength;
38 - COUNTER_DATA averageDiskQueueLength;
39 - COUNTER_DATA averageDiskReadQueueLength;
40 - COUNTER_DATA averageDiskWriteQueueLength;
41 - COUNTER_DATA averageDiskSecondsPerTransfer;
42 - COUNTER_DATA averageDiskSecondsPerRead;
43 - COUNTER_DATA averageDiskSecondsPerWrite;
44 - COUNTER_DATA diskTransfersPerSec;
45 - COUNTER_DATA diskReadsPerSec;
46 - COUNTER_DATA diskWritesPerSec;
47 - COUNTER_DATA diskBytesPerSec;
48 - COUNTER_DATA averageDiskBytesPerTransfer;
49 - COUNTER_DATA averageDiskBytesPerRead;
50 - COUNTER_DATA averageDiskBytesPerWrite;
51 - COUNTER_DATA splitIoPerSec;
52 -};
53 -
54 -struct physical_disk system_physical_total = {
55 - .collected_metadata = true,
56 -};
57 -
58 -void dict_logical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
59 - struct logical_disk *ld = value;
60 -
61 - ld->percentDiskFree.key = "% Free Space";
62 - // ld->freeMegabytes.key = "Free Megabytes";
63 -}
64 -
65 -void initialize_physical_disk(struct physical_disk *pd) {
66 - pd->percentIdleTime.key = "% Idle Time";
67 - pd->percentDiskTime.key = "% Disk Time";
68 - pd->percentDiskReadTime.key = "% Disk Read Time";
69 - pd->percentDiskWriteTime.key = "% Disk Write Time";
70 - pd->currentDiskQueueLength.key = "Current Disk Queue Length";
71 - pd->averageDiskQueueLength.key = "Avg. Disk Queue Length";
72 - pd->averageDiskReadQueueLength.key = "Avg. Disk Read Queue Length";
73 - pd->averageDiskWriteQueueLength.key = "Avg. Disk Write Queue Length";
74 - pd->averageDiskSecondsPerTransfer.key = "Avg. Disk sec/Transfer";
75 - pd->averageDiskSecondsPerRead.key = "Avg. Disk sec/Read";
76 - pd->averageDiskSecondsPerWrite.key = "Avg. Disk sec/Write";
77 - pd->diskTransfersPerSec.key = "Disk Transfers/sec";
78 - pd->diskReadsPerSec.key = "Disk Reads/sec";
79 - pd->diskWritesPerSec.key = "Disk Writes/sec";
80 - pd->diskBytesPerSec.key = "Disk Bytes/sec";
81 - pd->diskReadBytesPerSec.key = "Disk Read Bytes/sec";
82 - pd->diskWriteBytesPerSec.key = "Disk Write Bytes/sec";
83 - pd->averageDiskBytesPerTransfer.key = "Avg. Disk Bytes/Transfer";
84 - pd->averageDiskBytesPerRead.key = "Avg. Disk Bytes/Read";
85 - pd->averageDiskBytesPerWrite.key = "Avg. Disk Bytes/Write";
86 - pd->splitIoPerSec.key = "Split IO/Sec";
87 -}
88 -
89 -void dict_physical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
90 - struct physical_disk *pd = value;
91 - initialize_physical_disk(pd);
92 -}
93 -
94 -static DICTIONARY *logicalDisks = NULL, *physicalDisks = NULL;
95 -static void initialize(void) {
96 - initialize_physical_disk(&system_physical_total);
97 -
98 - logicalDisks = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
99 - DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct logical_disk));
100 -
101 - dictionary_register_insert_callback(logicalDisks, dict_logical_disk_insert_cb, NULL);
102 -
103 - physicalDisks = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
104 - DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct physical_disk));
105 -
106 - dictionary_register_insert_callback(physicalDisks, dict_physical_disk_insert_cb, NULL);
107 -}
108 -
109 -static STRING *getFileSystemType(const char* diskName) {
110 - if (!diskName || !*diskName) return NULL;
111 -
112 - char fileSystemNameBuffer[128] = {0}; // Buffer for file system name
113 - char pathBuffer[256] = {0}; // Path buffer to accommodate different formats
114 - DWORD serialNumber = 0;
115 - DWORD maxComponentLength = 0;
116 - DWORD fileSystemFlags = 0;
117 - BOOL success;
118 -
119 - // Check if the input is likely a drive letter (e.g., "C:")
120 - if (isalpha((uint8_t)diskName[0]) && diskName[1] == ':' && diskName[2] == '\0')
121 - snprintf(pathBuffer, sizeof(pathBuffer), "%s\\", diskName); // Format as "C:\\"
122 - else
123 - // Assume it's a Volume GUID path or a device path
124 - snprintf(pathBuffer, sizeof(pathBuffer), "\\\\.\\%s", diskName); // Format as "\\.\HarddiskVolume1"
125 -
126 - // Attempt to get the volume information
127 - success = GetVolumeInformation(
128 - pathBuffer, // Path to the disk
129 - NULL, // We don't need the volume name
130 - 0, // Size of volume name buffer is 0
131 - &serialNumber, // Volume serial number
132 - &maxComponentLength, // Maximum component length
133 - &fileSystemFlags, // File system flags
134 - fileSystemNameBuffer, // File system name buffer
135 - sizeof(fileSystemNameBuffer) // Size of file system name buffer
136 - );
137 -
138 - if (success && fileSystemNameBuffer[0]) {
139 - char *s = fileSystemNameBuffer;
140 - while(*s) { *s = tolower((uint8_t)*s); s++; }
141 - return string_strdupz(fileSystemNameBuffer); // Duplicate the file system name
142 - }
143 - else
144 - return NULL;
145 -}
146 -
147 -static bool do_logical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
148 - DICTIONARY *dict = logicalDisks;
149 -
150 - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "LogicalDisk");
151 - if(!pObjectType) return false;
152 -
153 - PERF_INSTANCE_DEFINITION *pi = NULL;
154 - for(LONG i = 0; i < pObjectType->NumInstances ; i++) {
155 - pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
156 - if(!pi) break;
157 -
158 - if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
159 - strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
160 -
161 - if(strcasecmp(windows_shared_buffer, "_Total") == 0)
162 - continue;
163 -
164 - struct logical_disk *d = dictionary_set(dict, windows_shared_buffer, NULL, sizeof(*d));
165 -
166 - if(!d->collected_metadata) {
167 - d->filesystem = getFileSystemType(windows_shared_buffer);
168 - d->collected_metadata = true;
169 - }
170 -
171 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskFree);
172 - // perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->freeMegabytes);
173 -
174 - if(!d->st_disk_space) {
175 - d->st_disk_space = rrdset_create_localhost(
176 - "disk_space"
177 - , windows_shared_buffer, NULL
178 - , windows_shared_buffer, "disk.space"
179 - , "Disk Space Usage"
180 - , "GiB"
181 - , PLUGIN_WINDOWS_NAME
182 - , "PerflibStorage"
183 - , NETDATA_CHART_PRIO_DISKSPACE_SPACE
184 - , update_every
185 - , RRDSET_TYPE_STACKED
186 - );
187 -
188 - rrdlabels_add(d->st_disk_space->rrdlabels, "mount_point", windows_shared_buffer, RRDLABEL_SRC_AUTO);
189 - // rrdlabels_add(d->st->rrdlabels, "mount_root", name, RRDLABEL_SRC_AUTO);
190 -
191 - if(d->filesystem)
192 - rrdlabels_add(d->st_disk_space->rrdlabels, "filesystem", string2str(d->filesystem), RRDLABEL_SRC_AUTO);
193 -
194 - d->rd_disk_space_free = rrddim_add(d->st_disk_space, "avail", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
195 - d->rd_disk_space_used = rrddim_add(d->st_disk_space, "used", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
196 - }
197 -
198 - // percentDiskFree has the free space in Data and the size of the disk in Time, in MiB.
199 - rrddim_set_by_pointer(d->st_disk_space, d->rd_disk_space_free, (collected_number)d->percentDiskFree.current.Data);
200 - rrddim_set_by_pointer(d->st_disk_space, d->rd_disk_space_used, (collected_number)(d->percentDiskFree.current.Time - d->percentDiskFree.current.Data));
201 - rrdset_done(d->st_disk_space);
202 - }
203 -
204 - return true;
205 -}
206 -
207 -static void physical_disk_labels(RRDSET *st, void *data) {
208 - struct physical_disk *d = data;
209 -
210 - if(d->device)
211 - rrdlabels_add(st->rrdlabels, "device", string2str(d->device), RRDLABEL_SRC_AUTO);
212 -
213 - if (d->mount_point)
214 - rrdlabels_add(st->rrdlabels, "mount_point", string2str(d->mount_point), RRDLABEL_SRC_AUTO);
215 -}
216 -
217 -static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
218 - DICTIONARY *dict = physicalDisks;
219 -
220 - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "PhysicalDisk");
221 - if(!pObjectType) return false;
222 -
223 - PERF_INSTANCE_DEFINITION *pi = NULL;
224 - for (LONG i = 0; i < pObjectType->NumInstances; i++) {
225 - pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
226 - if (!pi)
227 - break;
228 -
229 - if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
230 - strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
231 -
232 - char *device = windows_shared_buffer;
233 - char *mount_point = NULL;
234 -
235 - if((mount_point = strchr(device, ' '))) {
236 - *mount_point = '\0';
237 - mount_point++;
238 - }
239 -
240 - struct physical_disk *d;
241 - bool is_system;
242 - if (strcasecmp(windows_shared_buffer, "_Total") == 0) {
243 - d = &system_physical_total;
244 - is_system = true;
245 - }
246 - else {
247 - d = dictionary_set(dict, device, NULL, sizeof(*d));
248 - is_system = false;
249 - }
250 -
251 - if (!d->collected_metadata) {
252 - // TODO collect metadata - device_type, serial, id
253 - d->device = string_strdupz(device);
254 - d->mount_point = string_strdupz(mount_point);
255 - d->collected_metadata = true;
256 - }
257 -
258 - if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskReadBytesPerSec) &&
259 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskWriteBytesPerSec)) {
260 - if(is_system)
261 - common_system_io(d->diskReadBytesPerSec.current.Data, d->diskWriteBytesPerSec.current.Data, update_every);
262 - else
263 - common_disk_io(
264 - &d->disk_io,
265 - device,
266 - NULL,
267 - d->diskReadBytesPerSec.current.Data,
268 - d->diskWriteBytesPerSec.current.Data,
269 - update_every,
270 - physical_disk_labels,
271 - d);
272 - }
273 -
274 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentIdleTime);
275 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskTime);
276 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskReadTime);
277 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskWriteTime);
278 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->currentDiskQueueLength);
279 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskQueueLength);
280 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskReadQueueLength);
281 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskWriteQueueLength);
282 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerTransfer);
283 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerRead);
284 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerWrite);
285 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskTransfersPerSec);
286 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskReadsPerSec);
287 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskWritesPerSec);
288 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskBytesPerSec);
289 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerTransfer);
290 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerRead);
291 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerWrite);
292 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->splitIoPerSec);
293 - }
294 -
295 - return true;
296 -}
297 -
298 -int do_PerflibStorage(int update_every, usec_t dt __maybe_unused) {
299 - static bool initialized = false;
300 -
301 - if(unlikely(!initialized)) {
302 - initialize();
303 - initialized = true;
304 - }
305 -
306 - DWORD id = RegistryFindIDByName("LogicalDisk");
307 - if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
308 - return -1;
309 -
310 - PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
311 - if(!pDataBlock) return -1;
312 -
313 - do_logical_disk(pDataBlock, update_every);
314 - do_physical_disk(pDataBlock, update_every);
315 -
316 - return 0;
317 -}
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "windows_plugin.h"
4 +#include "windows-internals.h"
5 +
6 +#define _COMMON_PLUGIN_NAME PLUGIN_WINDOWS_NAME
7 +#define _COMMON_PLUGIN_MODULE_NAME "PerflibStorage"
8 +#include "../common-contexts/common-contexts.h"
9 +
10 +struct logical_disk {
11 + bool collected_metadata;
12 +
13 + STRING *filesystem;
14 +
15 + RRDSET *st_disk_space;
16 + RRDDIM *rd_disk_space_used;
17 + RRDDIM *rd_disk_space_free;
18 +
19 + COUNTER_DATA percentDiskFree;
20 + // COUNTER_DATA freeMegabytes;
21 +};
22 +
23 +struct physical_disk {
24 + bool collected_metadata;
25 +
26 + STRING *device;
27 + STRING *mount_point;
28 +
29 + ND_DISK_IO disk_io;
30 + COUNTER_DATA diskReadBytesPerSec;
31 + COUNTER_DATA diskWriteBytesPerSec;
32 +
33 + COUNTER_DATA percentIdleTime;
34 + COUNTER_DATA percentDiskTime;
35 + COUNTER_DATA percentDiskReadTime;
36 + COUNTER_DATA percentDiskWriteTime;
37 + COUNTER_DATA currentDiskQueueLength;
38 + COUNTER_DATA averageDiskQueueLength;
39 + COUNTER_DATA averageDiskReadQueueLength;
40 + COUNTER_DATA averageDiskWriteQueueLength;
41 + COUNTER_DATA averageDiskSecondsPerTransfer;
42 + COUNTER_DATA averageDiskSecondsPerRead;
43 + COUNTER_DATA averageDiskSecondsPerWrite;
44 + COUNTER_DATA diskTransfersPerSec;
45 + COUNTER_DATA diskReadsPerSec;
46 + COUNTER_DATA diskWritesPerSec;
47 + COUNTER_DATA diskBytesPerSec;
48 + COUNTER_DATA averageDiskBytesPerTransfer;
49 + COUNTER_DATA averageDiskBytesPerRead;
50 + COUNTER_DATA averageDiskBytesPerWrite;
51 + COUNTER_DATA splitIoPerSec;
52 +};
53 +
54 +struct physical_disk system_physical_total = {
55 + .collected_metadata = true,
56 +};
57 +
58 +void dict_logical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
59 + struct logical_disk *ld = value;
60 +
61 + ld->percentDiskFree.key = "% Free Space";
62 + // ld->freeMegabytes.key = "Free Megabytes";
63 +}
64 +
65 +void initialize_physical_disk(struct physical_disk *pd) {
66 + pd->percentIdleTime.key = "% Idle Time";
67 + pd->percentDiskTime.key = "% Disk Time";
68 + pd->percentDiskReadTime.key = "% Disk Read Time";
69 + pd->percentDiskWriteTime.key = "% Disk Write Time";
70 + pd->currentDiskQueueLength.key = "Current Disk Queue Length";
71 + pd->averageDiskQueueLength.key = "Avg. Disk Queue Length";
72 + pd->averageDiskReadQueueLength.key = "Avg. Disk Read Queue Length";
73 + pd->averageDiskWriteQueueLength.key = "Avg. Disk Write Queue Length";
74 + pd->averageDiskSecondsPerTransfer.key = "Avg. Disk sec/Transfer";
75 + pd->averageDiskSecondsPerRead.key = "Avg. Disk sec/Read";
76 + pd->averageDiskSecondsPerWrite.key = "Avg. Disk sec/Write";
77 + pd->diskTransfersPerSec.key = "Disk Transfers/sec";
78 + pd->diskReadsPerSec.key = "Disk Reads/sec";
79 + pd->diskWritesPerSec.key = "Disk Writes/sec";
80 + pd->diskBytesPerSec.key = "Disk Bytes/sec";
81 + pd->diskReadBytesPerSec.key = "Disk Read Bytes/sec";
82 + pd->diskWriteBytesPerSec.key = "Disk Write Bytes/sec";
83 + pd->averageDiskBytesPerTransfer.key = "Avg. Disk Bytes/Transfer";
84 + pd->averageDiskBytesPerRead.key = "Avg. Disk Bytes/Read";
85 + pd->averageDiskBytesPerWrite.key = "Avg. Disk Bytes/Write";
86 + pd->splitIoPerSec.key = "Split IO/Sec";
87 +}
88 +
89 +void dict_physical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
90 + struct physical_disk *pd = value;
91 + initialize_physical_disk(pd);
92 +}
93 +
94 +static DICTIONARY *logicalDisks = NULL, *physicalDisks = NULL;
95 +static void initialize(void) {
96 + initialize_physical_disk(&system_physical_total);
97 +
98 + logicalDisks = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
99 + DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct logical_disk));
100 +
101 + dictionary_register_insert_callback(logicalDisks, dict_logical_disk_insert_cb, NULL);
102 +
103 + physicalDisks = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
104 + DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct physical_disk));
105 +
106 + dictionary_register_insert_callback(physicalDisks, dict_physical_disk_insert_cb, NULL);
107 +}
108 +
109 +static STRING *getFileSystemType(const char* diskName) {
110 + if (!diskName || !*diskName) return NULL;
111 +
112 + char fileSystemNameBuffer[128] = {0}; // Buffer for file system name
113 + char pathBuffer[256] = {0}; // Path buffer to accommodate different formats
114 + DWORD serialNumber = 0;
115 + DWORD maxComponentLength = 0;
116 + DWORD fileSystemFlags = 0;
117 + BOOL success;
118 +
119 + // Check if the input is likely a drive letter (e.g., "C:")
120 + if (isalpha((uint8_t)diskName[0]) && diskName[1] == ':' && diskName[2] == '\0')
121 + snprintf(pathBuffer, sizeof(pathBuffer), "%s\\", diskName); // Format as "C:\\"
122 + else
123 + // Assume it's a Volume GUID path or a device path
124 + snprintf(pathBuffer, sizeof(pathBuffer), "\\\\.\\%s", diskName); // Format as "\\.\HarddiskVolume1"
125 +
126 + // Attempt to get the volume information
127 + success = GetVolumeInformation(
128 + pathBuffer, // Path to the disk
129 + NULL, // We don't need the volume name
130 + 0, // Size of volume name buffer is 0
131 + &serialNumber, // Volume serial number
132 + &maxComponentLength, // Maximum component length
133 + &fileSystemFlags, // File system flags
134 + fileSystemNameBuffer, // File system name buffer
135 + sizeof(fileSystemNameBuffer) // Size of file system name buffer
136 + );
137 +
138 + if (success && fileSystemNameBuffer[0]) {
139 + char *s = fileSystemNameBuffer;
140 + while(*s) { *s = tolower((uint8_t)*s); s++; }
141 + return string_strdupz(fileSystemNameBuffer); // Duplicate the file system name
142 + }
143 + else
144 + return NULL;
145 +}
146 +
147 +static bool do_logical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
148 + DICTIONARY *dict = logicalDisks;
149 +
150 + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "LogicalDisk");
151 + if(!pObjectType) return false;
152 +
153 + PERF_INSTANCE_DEFINITION *pi = NULL;
154 + for(LONG i = 0; i < pObjectType->NumInstances ; i++) {
155 + pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
156 + if(!pi) break;
157 +
158 + if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
159 + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
160 +
161 + if(strcasecmp(windows_shared_buffer, "_Total") == 0)
162 + continue;
163 +
164 + struct logical_disk *d = dictionary_set(dict, windows_shared_buffer, NULL, sizeof(*d));
165 +
166 + if(!d->collected_metadata) {
167 + d->filesystem = getFileSystemType(windows_shared_buffer);
168 + d->collected_metadata = true;
169 + }
170 +
171 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskFree);
172 + // perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->freeMegabytes);
173 +
174 + if(!d->st_disk_space) {
175 + d->st_disk_space = rrdset_create_localhost(
176 + "disk_space"
177 + , windows_shared_buffer, NULL
178 + , windows_shared_buffer, "disk.space"
179 + , "Disk Space Usage"
180 + , "GiB"
181 + , PLUGIN_WINDOWS_NAME
182 + , "PerflibStorage"
183 + , NETDATA_CHART_PRIO_DISKSPACE_SPACE
184 + , update_every
185 + , RRDSET_TYPE_STACKED
186 + );
187 +
188 + rrdlabels_add(d->st_disk_space->rrdlabels, "mount_point", windows_shared_buffer, RRDLABEL_SRC_AUTO);
189 + // rrdlabels_add(d->st->rrdlabels, "mount_root", name, RRDLABEL_SRC_AUTO);
190 +
191 + if(d->filesystem)
192 + rrdlabels_add(d->st_disk_space->rrdlabels, "filesystem", string2str(d->filesystem), RRDLABEL_SRC_AUTO);
193 +
194 + d->rd_disk_space_free = rrddim_add(d->st_disk_space, "avail", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
195 + d->rd_disk_space_used = rrddim_add(d->st_disk_space, "used", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
196 + }
197 +
198 + // percentDiskFree has the free space in Data and the size of the disk in Time, in MiB.
199 + rrddim_set_by_pointer(d->st_disk_space, d->rd_disk_space_free, (collected_number)d->percentDiskFree.current.Data);
200 + rrddim_set_by_pointer(d->st_disk_space, d->rd_disk_space_used, (collected_number)(d->percentDiskFree.current.Time - d->percentDiskFree.current.Data));
201 + rrdset_done(d->st_disk_space);
202 + }
203 +
204 + return true;
205 +}
206 +
207 +static void physical_disk_labels(RRDSET *st, void *data) {
208 + struct physical_disk *d = data;
209 +
210 + if(d->device)
211 + rrdlabels_add(st->rrdlabels, "device", string2str(d->device), RRDLABEL_SRC_AUTO);
212 +
213 + if (d->mount_point)
214 + rrdlabels_add(st->rrdlabels, "mount_point", string2str(d->mount_point), RRDLABEL_SRC_AUTO);
215 +}
216 +
217 +static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every) {
218 + DICTIONARY *dict = physicalDisks;
219 +
220 + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "PhysicalDisk");
221 + if(!pObjectType) return false;
222 +
223 + PERF_INSTANCE_DEFINITION *pi = NULL;
224 + for (LONG i = 0; i < pObjectType->NumInstances; i++) {
225 + pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
226 + if (!pi)
227 + break;
228 +
229 + if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
230 + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
231 +
232 + char *device = windows_shared_buffer;
233 + char *mount_point = NULL;
234 +
235 + if((mount_point = strchr(device, ' '))) {
236 + *mount_point = '\0';
237 + mount_point++;
238 + }
239 +
240 + struct physical_disk *d;
241 + bool is_system;
242 + if (strcasecmp(windows_shared_buffer, "_Total") == 0) {
243 + d = &system_physical_total;
244 + is_system = true;
245 + }
246 + else {
247 + d = dictionary_set(dict, device, NULL, sizeof(*d));
248 + is_system = false;
249 + }
250 +
251 + if (!d->collected_metadata) {
252 + // TODO collect metadata - device_type, serial, id
253 + d->device = string_strdupz(device);
254 + d->mount_point = string_strdupz(mount_point);
255 + d->collected_metadata = true;
256 + }
257 +
258 + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskReadBytesPerSec) &&
259 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskWriteBytesPerSec)) {
260 + if(is_system)
261 + common_system_io(d->diskReadBytesPerSec.current.Data, d->diskWriteBytesPerSec.current.Data, update_every);
262 + else
263 + common_disk_io(
264 + &d->disk_io,
265 + device,
266 + NULL,
267 + d->diskReadBytesPerSec.current.Data,
268 + d->diskWriteBytesPerSec.current.Data,
269 + update_every,
270 + physical_disk_labels,
271 + d);
272 + }
273 +
274 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentIdleTime);
275 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskTime);
276 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskReadTime);
277 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentDiskWriteTime);
278 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->currentDiskQueueLength);
279 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskQueueLength);
280 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskReadQueueLength);
281 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskWriteQueueLength);
282 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerTransfer);
283 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerRead);
284 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerWrite);
285 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskTransfersPerSec);
286 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskReadsPerSec);
287 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskWritesPerSec);
288 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskBytesPerSec);
289 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerTransfer);
290 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerRead);
291 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerWrite);
292 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->splitIoPerSec);
293 + }
294 +
295 + return true;
296 +}
297 +
298 +int do_PerflibStorage(int update_every, usec_t dt __maybe_unused) {
299 + static bool initialized = false;
300 +
301 + if(unlikely(!initialized)) {
302 + initialize();
303 + initialized = true;
304 + }
305 +
306 + DWORD id = RegistryFindIDByName("LogicalDisk");
307 + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
308 + return -1;
309 +
310 + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
311 + if(!pDataBlock) return -1;
312 +
313 + do_logical_disk(pDataBlock, update_every);
314 + do_physical_disk(pDataBlock, update_every);
315 +
316 + return 0;
317 +}
src/collectors/windows.plugin/perflib-thermalzone.c
+105 -105
@@ -1,105 +1,105 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "windows_plugin.h"
4 -#include "windows-internals.h"
5 -
6 -typedef struct thermal_zone {
7 - RRDSET *st;
8 - RRDDIM *rd;
9 -
10 - COUNTER_DATA thermalZoneTemperature;
11 -};
12 -
13 -static inline void initialize_thermal_zone_keys(struct thermal_zone *p) {
14 - p->thermalZoneTemperature.key = "Temperature";
15 -}
16 -
17 -void dict_thermal_zone_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
18 - struct thermal_zone *p = value;
19 - initialize_thermal_zone_keys(p);
20 -}
21 -
22 -static DICTIONARY *thermal_zones = NULL;
23 -
24 -static void initialize(void) {
25 - thermal_zones = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
26 - DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct thermal_zone));
27 -
28 - dictionary_register_insert_callback(thermal_zones, dict_thermal_zone_insert_cb, NULL);
29 -}
30 -
31 -static bool do_thermal_zones(PERF_DATA_BLOCK *pDataBlock, int update_every) {
32 - PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Thermal Zone Information");
33 - if(!pObjectType) return false;
34 -
35 - PERF_INSTANCE_DEFINITION *pi = NULL;
36 - for(LONG i = 0; i < pObjectType->NumInstances ; i++) {
37 - pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
38 - if(!pi) break;
39 -
40 - if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
41 - strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
42 -
43 - netdata_fix_chart_name(windows_shared_buffer);
44 - struct thermal_zone *p = dictionary_set(thermal_zones, windows_shared_buffer, NULL, sizeof(*p));
45 -
46 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->thermalZoneTemperature);
47 -
48 - // https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/design-guide
49 - if(!p->st) {
50 - char id[RRD_ID_LENGTH_MAX + 1];
51 - snprintfz(id, RRD_ID_LENGTH_MAX, "thermalzone_%s_temperature", windows_shared_buffer);
52 - p->st = rrdset_create_localhost(
53 - "system"
54 - , id, NULL
55 - , "thermalzone"
56 - , "system.thermalzone_temperature"
57 - , "Thermal zone temperature"
58 - , "Celsius"
59 - , PLUGIN_WINDOWS_NAME
60 - , "ThermalZone"
61 - , NETDATA_CHART_PRIO_WINDOWS_THERMAL_ZONES
62 - , update_every
63 - , RRDSET_TYPE_LINE
64 - );
65 -
66 - p->rd = rrddim_add(p->st,
67 - id,
68 - "temperature",
69 - 1,
70 - 1,
71 - RRD_ALGORITHM_ABSOLUTE);
72 -
73 - rrdlabels_add(p->st->rrdlabels, "thermalzone", windows_shared_buffer, RRDLABEL_SRC_AUTO);
74 - }
75 -
76 - // Convert to Celsius before to plot
77 - NETDATA_DOUBLE kTemperature = (NETDATA_DOUBLE)p->thermalZoneTemperature.current.Data;
78 - kTemperature -= 273.15;
79 -
80 - rrddim_set_by_pointer(p->st, p->rd, (collected_number)kTemperature);
81 - rrdset_done(p->st);
82 - }
83 -
84 - return true;
85 -}
86 -
87 -int do_PerflibThermalZone(int update_every, usec_t dt __maybe_unused) {
88 - static bool initialized = false;
89 -
90 - if(unlikely(!initialized)) {
91 - initialize();
92 - initialized = true;
93 - }
94 -
95 - DWORD id = RegistryFindIDByName("Thermal Zone Information");
96 - if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
97 - return -1;
98 -
99 - PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
100 - if(!pDataBlock) return -1;
101 -
102 - do_thermal_zones(pDataBlock, update_every);
103 -
104 - return 0;
105 -}
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "windows_plugin.h"
4 +#include "windows-internals.h"
5 +
6 +struct thermal_zone {
7 + RRDSET *st;
8 + RRDDIM *rd;
9 +
10 + COUNTER_DATA thermalZoneTemperature;
11 +};
12 +
13 +static inline void initialize_thermal_zone_keys(struct thermal_zone *p) {
14 + p->thermalZoneTemperature.key = "Temperature";
15 +}
16 +
17 +void dict_thermal_zone_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
18 + struct thermal_zone *p = value;
19 + initialize_thermal_zone_keys(p);
20 +}
21 +
22 +static DICTIONARY *thermal_zones = NULL;
23 +
24 +static void initialize(void) {
25 + thermal_zones = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE |
26 + DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct thermal_zone));
27 +
28 + dictionary_register_insert_callback(thermal_zones, dict_thermal_zone_insert_cb, NULL);
29 +}
30 +
31 +static bool do_thermal_zones(PERF_DATA_BLOCK *pDataBlock, int update_every) {
32 + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, "Thermal Zone Information");
33 + if(!pObjectType) return false;
34 +
35 + PERF_INSTANCE_DEFINITION *pi = NULL;
36 + for(LONG i = 0; i < pObjectType->NumInstances ; i++) {
37 + pi = perflibForEachInstance(pDataBlock, pObjectType, pi);
38 + if(!pi) break;
39 +
40 + if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
41 + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
42 +
43 + netdata_fix_chart_name(windows_shared_buffer);
44 + struct thermal_zone *p = dictionary_set(thermal_zones, windows_shared_buffer, NULL, sizeof(*p));
45 +
46 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &p->thermalZoneTemperature);
47 +
48 + // https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/design-guide
49 + if(!p->st) {
50 + char id[RRD_ID_LENGTH_MAX + 1];
51 + snprintfz(id, RRD_ID_LENGTH_MAX, "thermalzone_%s_temperature", windows_shared_buffer);
52 + p->st = rrdset_create_localhost(
53 + "system"
54 + , id, NULL
55 + , "thermalzone"
56 + , "system.thermalzone_temperature"
57 + , "Thermal zone temperature"
58 + , "Celsius"
59 + , PLUGIN_WINDOWS_NAME
60 + , "ThermalZone"
61 + , NETDATA_CHART_PRIO_WINDOWS_THERMAL_ZONES
62 + , update_every
63 + , RRDSET_TYPE_LINE
64 + );
65 +
66 + p->rd = rrddim_add(p->st,
67 + id,
68 + "temperature",
69 + 1,
70 + 1,
71 + RRD_ALGORITHM_ABSOLUTE);
72 +
73 + rrdlabels_add(p->st->rrdlabels, "thermalzone", windows_shared_buffer, RRDLABEL_SRC_AUTO);
74 + }
75 +
76 + // Convert to Celsius before to plot
77 + NETDATA_DOUBLE kTemperature = (NETDATA_DOUBLE)p->thermalZoneTemperature.current.Data;
78 + kTemperature -= 273.15;
79 +
80 + rrddim_set_by_pointer(p->st, p->rd, (collected_number)kTemperature);
81 + rrdset_done(p->st);
82 + }
83 +
84 + return true;
85 +}
86 +
87 +int do_PerflibThermalZone(int update_every, usec_t dt __maybe_unused) {
88 + static bool initialized = false;
89 +
90 + if(unlikely(!initialized)) {
91 + initialize();
92 + initialized = true;
93 + }
94 +
95 + DWORD id = RegistryFindIDByName("Thermal Zone Information");
96 + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND)
97 + return -1;
98 +
99 + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
100 + if(!pDataBlock) return -1;
101 +
102 + do_thermal_zones(pDataBlock, update_every);
103 +
104 + return 0;
105 +}
src/collectors/windows.plugin/windows-internals.h
+17 -18
@@ -1,18 +1,17 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_WINDOWS_INTERNALS_H
4 -#define NETDATA_WINDOWS_INTERNALS_H
5 -
6 -#include <windows.h>
7 -
8 -static inline ULONGLONG FileTimeToULL(FILETIME ft) {
9 - ULARGE_INTEGER ul;
10 - ul.LowPart = ft.dwLowDateTime;
11 - ul.HighPart = ft.dwHighDateTime;
12 - return ul.QuadPart;
13 -}
14 -
15 -#include "perflib.h"
16 -#include "perflib-rrd.h"
17 -
18 -#endif //NETDATA_WINDOWS_INTERNALS_H
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_WINDOWS_INTERNALS_H
4 +#define NETDATA_WINDOWS_INTERNALS_H
5 +
6 +#include <windows.h>
7 +
8 +static inline ULONGLONG FileTimeToULL(FILETIME ft) {
9 + ULARGE_INTEGER ul;
10 + ul.LowPart = ft.dwLowDateTime;
11 + ul.HighPart = ft.dwHighDateTime;
12 + return ul.QuadPart;
13 +}
14 +
15 +#include "perflib-rrd.h"
16 +
17 +#endif //NETDATA_WINDOWS_INTERNALS_H
src/collectors/windows.plugin/windows_plugin.h
-2
@@ -26,6 +26,4 @@ int do_PerflibMemory(int update_every, usec_t dt);
26 int do_PerflibObjects(int update_every, usec_t dt);
27 int do_PerflibThermalZone(int update_every, usec_t dt);
28
29 -#include "perflib.h"
30 -
29 #endif //NETDATA_WINDOWS_PLUGIN_H
src/libnetdata/libnetdata.c
-530
@@ -478,536 +478,6 @@ void json_fix_string(char *s) {
478 }
479 }
480
481 -unsigned char netdata_map_chart_names[256] = {
482 - [0] = '\0', //
483 - [1] = '_', //
484 - [2] = '_', //
485 - [3] = '_', //
486 - [4] = '_', //
487 - [5] = '_', //
488 - [6] = '_', //
489 - [7] = '_', //
490 - [8] = '_', //
491 - [9] = '_', //
492 - [10] = '_', //
493 - [11] = '_', //
494 - [12] = '_', //
495 - [13] = '_', //
496 - [14] = '_', //
497 - [15] = '_', //
498 - [16] = '_', //
499 - [17] = '_', //
500 - [18] = '_', //
501 - [19] = '_', //
502 - [20] = '_', //
503 - [21] = '_', //
504 - [22] = '_', //
505 - [23] = '_', //
506 - [24] = '_', //
507 - [25] = '_', //
508 - [26] = '_', //
509 - [27] = '_', //
510 - [28] = '_', //
511 - [29] = '_', //
512 - [30] = '_', //
513 - [31] = '_', //
514 - [32] = '_', //
515 - [33] = '_', // !
516 - [34] = '_', // "
517 - [35] = '_', // #
518 - [36] = '_', // $
519 - [37] = '_', // %
520 - [38] = '_', // &
521 - [39] = '_', // '
522 - [40] = '_', // (
523 - [41] = '_', // )
524 - [42] = '_', // *
525 - [43] = '_', // +
526 - [44] = '.', // ,
527 - [45] = '-', // -
528 - [46] = '.', // .
529 - [47] = '/', // /
530 - [48] = '0', // 0
531 - [49] = '1', // 1
532 - [50] = '2', // 2
533 - [51] = '3', // 3
534 - [52] = '4', // 4
535 - [53] = '5', // 5
536 - [54] = '6', // 6
537 - [55] = '7', // 7
538 - [56] = '8', // 8
539 - [57] = '9', // 9
540 - [58] = '_', // :
541 - [59] = '_', // ;
542 - [60] = '_', // <
543 - [61] = '_', // =
544 - [62] = '_', // >
545 - [63] = '_', // ?
546 - [64] = '_', // @
547 - [65] = 'a', // A
548 - [66] = 'b', // B
549 - [67] = 'c', // C
550 - [68] = 'd', // D
551 - [69] = 'e', // E
552 - [70] = 'f', // F
553 - [71] = 'g', // G
554 - [72] = 'h', // H
555 - [73] = 'i', // I
556 - [74] = 'j', // J
557 - [75] = 'k', // K
558 - [76] = 'l', // L
559 - [77] = 'm', // M
560 - [78] = 'n', // N
561 - [79] = 'o', // O
562 - [80] = 'p', // P
563 - [81] = 'q', // Q
564 - [82] = 'r', // R
565 - [83] = 's', // S
566 - [84] = 't', // T
567 - [85] = 'u', // U
568 - [86] = 'v', // V
569 - [87] = 'w', // W
570 - [88] = 'x', // X
571 - [89] = 'y', // Y
572 - [90] = 'z', // Z
573 - [91] = '_', // [
574 - [92] = '/', // backslash
575 - [93] = '_', // ]
576 - [94] = '_', // ^
577 - [95] = '_', // _
578 - [96] = '_', // `
579 - [97] = 'a', // a
580 - [98] = 'b', // b
581 - [99] = 'c', // c
582 - [100] = 'd', // d
583 - [101] = 'e', // e
584 - [102] = 'f', // f
585 - [103] = 'g', // g
586 - [104] = 'h', // h
587 - [105] = 'i', // i
588 - [106] = 'j', // j
589 - [107] = 'k', // k
590 - [108] = 'l', // l
591 - [109] = 'm', // m
592 - [110] = 'n', // n
593 - [111] = 'o', // o
594 - [112] = 'p', // p
595 - [113] = 'q', // q
596 - [114] = 'r', // r
597 - [115] = 's', // s
598 - [116] = 't', // t
599 - [117] = 'u', // u
600 - [118] = 'v', // v
601 - [119] = 'w', // w
602 - [120] = 'x', // x
603 - [121] = 'y', // y
604 - [122] = 'z', // z
605 - [123] = '_', // {
606 - [124] = '_', // |
607 - [125] = '_', // }
608 - [126] = '_', // ~
609 - [127] = '_', //
610 - [128] = '_', //
611 - [129] = '_', //
612 - [130] = '_', //
613 - [131] = '_', //
614 - [132] = '_', //
615 - [133] = '_', //
616 - [134] = '_', //
617 - [135] = '_', //
618 - [136] = '_', //
619 - [137] = '_', //
620 - [138] = '_', //
621 - [139] = '_', //
622 - [140] = '_', //
623 - [141] = '_', //
624 - [142] = '_', //
625 - [143] = '_', //
626 - [144] = '_', //
627 - [145] = '_', //
628 - [146] = '_', //
629 - [147] = '_', //
630 - [148] = '_', //
631 - [149] = '_', //
632 - [150] = '_', //
633 - [151] = '_', //
634 - [152] = '_', //
635 - [153] = '_', //
636 - [154] = '_', //
637 - [155] = '_', //
638 - [156] = '_', //
639 - [157] = '_', //
640 - [158] = '_', //
641 - [159] = '_', //
642 - [160] = '_', //
643 - [161] = '_', //
644 - [162] = '_', //
645 - [163] = '_', //
646 - [164] = '_', //
647 - [165] = '_', //
648 - [166] = '_', //
649 - [167] = '_', //
650 - [168] = '_', //
651 - [169] = '_', //
652 - [170] = '_', //
653 - [171] = '_', //
654 - [172] = '_', //
655 - [173] = '_', //
656 - [174] = '_', //
657 - [175] = '_', //
658 - [176] = '_', //
659 - [177] = '_', //
660 - [178] = '_', //
661 - [179] = '_', //
662 - [180] = '_', //
663 - [181] = '_', //
664 - [182] = '_', //
665 - [183] = '_', //
666 - [184] = '_', //
667 - [185] = '_', //
668 - [186] = '_', //
669 - [187] = '_', //
670 - [188] = '_', //
671 - [189] = '_', //
672 - [190] = '_', //
673 - [191] = '_', //
674 - [192] = '_', //
675 - [193] = '_', //
676 - [194] = '_', //
677 - [195] = '_', //
678 - [196] = '_', //
679 - [197] = '_', //
680 - [198] = '_', //
681 - [199] = '_', //
682 - [200] = '_', //
683 - [201] = '_', //
684 - [202] = '_', //
685 - [203] = '_', //
686 - [204] = '_', //
687 - [205] = '_', //
688 - [206] = '_', //
689 - [207] = '_', //
690 - [208] = '_', //
691 - [209] = '_', //
692 - [210] = '_', //
693 - [211] = '_', //
694 - [212] = '_', //
695 - [213] = '_', //
696 - [214] = '_', //
697 - [215] = '_', //
698 - [216] = '_', //
699 - [217] = '_', //
700 - [218] = '_', //
701 - [219] = '_', //
702 - [220] = '_', //
703 - [221] = '_', //
704 - [222] = '_', //
705 - [223] = '_', //
706 - [224] = '_', //
707 - [225] = '_', //
708 - [226] = '_', //
709 - [227] = '_', //
710 - [228] = '_', //
711 - [229] = '_', //
712 - [230] = '_', //
713 - [231] = '_', //
714 - [232] = '_', //
715 - [233] = '_', //
716 - [234] = '_', //
717 - [235] = '_', //
718 - [236] = '_', //
719 - [237] = '_', //
720 - [238] = '_', //
721 - [239] = '_', //
722 - [240] = '_', //
723 - [241] = '_', //
724 - [242] = '_', //
725 - [243] = '_', //
726 - [244] = '_', //
727 - [245] = '_', //
728 - [246] = '_', //
729 - [247] = '_', //
730 - [248] = '_', //
731 - [249] = '_', //
732 - [250] = '_', //
733 - [251] = '_', //
734 - [252] = '_', //
735 - [253] = '_', //
736 - [254] = '_', //
737 - [255] = '_' //
738 -};
739 -
740 -// make sure the supplied string
741 -// is good for a netdata chart/dimension ID/NAME
742 -void netdata_fix_chart_name(char *s) {
743 - while ((*s = netdata_map_chart_names[(unsigned char) *s])) s++;
744 -}
745 -
746 -unsigned char netdata_map_chart_ids[256] = {
747 - [0] = '\0', //
748 - [1] = '_', //
749 - [2] = '_', //
750 - [3] = '_', //
751 - [4] = '_', //
752 - [5] = '_', //
753 - [6] = '_', //
754 - [7] = '_', //
755 - [8] = '_', //
756 - [9] = '_', //
757 - [10] = '_', //
758 - [11] = '_', //
759 - [12] = '_', //
760 - [13] = '_', //
761 - [14] = '_', //
762 - [15] = '_', //
763 - [16] = '_', //
764 - [17] = '_', //
765 - [18] = '_', //
766 - [19] = '_', //
767 - [20] = '_', //
768 - [21] = '_', //
769 - [22] = '_', //
770 - [23] = '_', //
771 - [24] = '_', //
772 - [25] = '_', //
773 - [26] = '_', //
774 - [27] = '_', //
775 - [28] = '_', //
776 - [29] = '_', //
777 - [30] = '_', //
778 - [31] = '_', //
779 - [32] = '_', //
780 - [33] = '_', // !
781 - [34] = '_', // "
782 - [35] = '_', // #
783 - [36] = '_', // $
784 - [37] = '_', // %
785 - [38] = '_', // &
786 - [39] = '_', // '
787 - [40] = '_', // (
788 - [41] = '_', // )
789 - [42] = '_', // *
790 - [43] = '_', // +
791 - [44] = '.', // ,
792 - [45] = '-', // -
793 - [46] = '.', // .
794 - [47] = '_', // /
795 - [48] = '0', // 0
796 - [49] = '1', // 1
797 - [50] = '2', // 2
798 - [51] = '3', // 3
799 - [52] = '4', // 4
800 - [53] = '5', // 5
801 - [54] = '6', // 6
802 - [55] = '7', // 7
803 - [56] = '8', // 8
804 - [57] = '9', // 9
805 - [58] = '_', // :
806 - [59] = '_', // ;
807 - [60] = '_', // <
808 - [61] = '_', // =
809 - [62] = '_', // >
810 - [63] = '_', // ?
811 - [64] = '_', // @
812 - [65] = 'a', // A
813 - [66] = 'b', // B
814 - [67] = 'c', // C
815 - [68] = 'd', // D
816 - [69] = 'e', // E
817 - [70] = 'f', // F
818 - [71] = 'g', // G
819 - [72] = 'h', // H
820 - [73] = 'i', // I
821 - [74] = 'j', // J
822 - [75] = 'k', // K
823 - [76] = 'l', // L
824 - [77] = 'm', // M
825 - [78] = 'n', // N
826 - [79] = 'o', // O
827 - [80] = 'p', // P
828 - [81] = 'q', // Q
829 - [82] = 'r', // R
830 - [83] = 's', // S
831 - [84] = 't', // T
832 - [85] = 'u', // U
833 - [86] = 'v', // V
834 - [87] = 'w', // W
835 - [88] = 'x', // X
836 - [89] = 'y', // Y
837 - [90] = 'z', // Z
838 - [91] = '_', // [
839 - [92] = '_', // backslash
840 - [93] = '_', // ]
841 - [94] = '_', // ^
842 - [95] = '_', // _
843 - [96] = '_', // `
844 - [97] = 'a', // a
845 - [98] = 'b', // b
846 - [99] = 'c', // c
847 - [100] = 'd', // d
848 - [101] = 'e', // e
849 - [102] = 'f', // f
850 - [103] = 'g', // g
851 - [104] = 'h', // h
852 - [105] = 'i', // i
853 - [106] = 'j', // j
854 - [107] = 'k', // k
855 - [108] = 'l', // l
856 - [109] = 'm', // m
857 - [110] = 'n', // n
858 - [111] = 'o', // o
859 - [112] = 'p', // p
860 - [113] = 'q', // q
861 - [114] = 'r', // r
862 - [115] = 's', // s
863 - [116] = 't', // t
864 - [117] = 'u', // u
865 - [118] = 'v', // v
866 - [119] = 'w', // w
867 - [120] = 'x', // x
868 - [121] = 'y', // y
869 - [122] = 'z', // z
870 - [123] = '_', // {
871 - [124] = '_', // |
872 - [125] = '_', // }
873 - [126] = '_', // ~
874 - [127] = '_', //
875 - [128] = '_', //
876 - [129] = '_', //
877 - [130] = '_', //
878 - [131] = '_', //
879 - [132] = '_', //
880 - [133] = '_', //
881 - [134] = '_', //
882 - [135] = '_', //
883 - [136] = '_', //
884 - [137] = '_', //
885 - [138] = '_', //
886 - [139] = '_', //
887 - [140] = '_', //
888 - [141] = '_', //
889 - [142] = '_', //
890 - [143] = '_', //
891 - [144] = '_', //
892 - [145] = '_', //
893 - [146] = '_', //
894 - [147] = '_', //
895 - [148] = '_', //
896 - [149] = '_', //
897 - [150] = '_', //
898 - [151] = '_', //
899 - [152] = '_', //
900 - [153] = '_', //
901 - [154] = '_', //
902 - [155] = '_', //
903 - [156] = '_', //
904 - [157] = '_', //
905 - [158] = '_', //
906 - [159] = '_', //
907 - [160] = '_', //
908 - [161] = '_', //
909 - [162] = '_', //
910 - [163] = '_', //
911 - [164] = '_', //
912 - [165] = '_', //
913 - [166] = '_', //
914 - [167] = '_', //
915 - [168] = '_', //
916 - [169] = '_', //
917 - [170] = '_', //
918 - [171] = '_', //
919 - [172] = '_', //
920 - [173] = '_', //
921 - [174] = '_', //
922 - [175] = '_', //
923 - [176] = '_', //
924 - [177] = '_', //
925 - [178] = '_', //
926 - [179] = '_', //
927 - [180] = '_', //
928 - [181] = '_', //
929 - [182] = '_', //
930 - [183] = '_', //
931 - [184] = '_', //
932 - [185] = '_', //
933 - [186] = '_', //
934 - [187] = '_', //
935 - [188] = '_', //
936 - [189] = '_', //
937 - [190] = '_', //
938 - [191] = '_', //
939 - [192] = '_', //
940 - [193] = '_', //
941 - [194] = '_', //
942 - [195] = '_', //
943 - [196] = '_', //
944 - [197] = '_', //
945 - [198] = '_', //
946 - [199] = '_', //
947 - [200] = '_', //
948 - [201] = '_', //
949 - [202] = '_', //
950 - [203] = '_', //
951 - [204] = '_', //
952 - [205] = '_', //
953 - [206] = '_', //
954 - [207] = '_', //
955 - [208] = '_', //
956 - [209] = '_', //
957 - [210] = '_', //
958 - [211] = '_', //
959 - [212] = '_', //
960 - [213] = '_', //
961 - [214] = '_', //
962 - [215] = '_', //
963 - [216] = '_', //
964 - [217] = '_', //
965 - [218] = '_', //
966 - [219] = '_', //
967 - [220] = '_', //
968 - [221] = '_', //
969 - [222] = '_', //
970 - [223] = '_', //
971 - [224] = '_', //
972 - [225] = '_', //
973 - [226] = '_', //
974 - [227] = '_', //
975 - [228] = '_', //
976 - [229] = '_', //
977 - [230] = '_', //
978 - [231] = '_', //
979 - [232] = '_', //
980 - [233] = '_', //
981 - [234] = '_', //
982 - [235] = '_', //
983 - [236] = '_', //
984 - [237] = '_', //
985 - [238] = '_', //
986 - [239] = '_', //
987 - [240] = '_', //
988 - [241] = '_', //
989 - [242] = '_', //
990 - [243] = '_', //
991 - [244] = '_', //
992 - [245] = '_', //
993 - [246] = '_', //
994 - [247] = '_', //
995 - [248] = '_', //
996 - [249] = '_', //
997 - [250] = '_', //
998 - [251] = '_', //
999 - [252] = '_', //
1000 - [253] = '_', //
1001 - [254] = '_', //
1002 - [255] = '_' //
1003 -};
1004 -
1005 -// make sure the supplied string
1006 -// is good for a netdata chart/dimension ID/NAME
1007 -void netdata_fix_chart_id(char *s) {
1008 - while ((*s = netdata_map_chart_ids[(unsigned char) *s])) s++;
1009 -}
1010 -
481 static int memory_file_open(const char *filename, size_t size) {
482 // netdata_log_info("memory_file_open('%s', %zu", filename, size);
483
src/libnetdata/libnetdata.h
+2 -3
@@ -327,9 +327,6 @@ typedef uint32_t uid_t;
327 #include "storage-point.h"
328 #include "paths/paths.h"
329
330 -void netdata_fix_chart_id(char *s);
331 -void netdata_fix_chart_name(char *s);
332 -
330 int madvise_sequential(void *mem, size_t len);
331 int madvise_random(void *mem, size_t len);
332 int madvise_dontfork(void *mem, size_t len);
@@ -475,6 +472,8 @@ extern const char *netdata_configured_host_prefix;
472 #include "string/string.h"
473 #include "dictionary/dictionary.h"
474 #include "dictionary/thread-cache.h"
475 +#include "sanitizers/chart_id_and_name.h"
476 +
477 #if defined(HAVE_LIBBPF) && !defined(__cplusplus)
478 #include "ebpf/ebpf.h"
479 #endif
src/libnetdata/os/get_system_cpus.c
+8 -1
@@ -82,7 +82,14 @@ long os_get_system_cpus_cached(bool cache, bool for_netdata) {
82
83 SYSTEM_INFO sysInfo;
84 GetSystemInfo(&sysInfo);
85 - return (long) sysInfo.dwNumberOfProcessors;
85 + processors[index] = sysInfo.dwNumberOfProcessors;
86 +
87 + if(processors[index] < 1) {
88 + processors[index] = 1;
89 + netdata_log_error("Assuming system has %ld processors.", processors[index]);
90 + }
91 +
92 + return processors[index];
93
94 #else
95
src/libnetdata/os/os.h
+1
@@ -23,6 +23,7 @@
23 #include "os-freebsd-wrappers.h"
24 #include "os-macos-wrappers.h"
25 #include "os-windows-wrappers.h"
26 +#include "windows-perflib/perflib.h"
27
28 // =====================================================================================================================
29 // common defs for Apple/FreeBSD/Linux
src/libnetdata/os/windows-perflib/perflib-dump.c renamed
+533 -529
@@ -1,529 +1,533 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "perflib.h"
4 -#include "windows-internals.h"
5 -
6 -static const char *getCounterType(DWORD CounterType) {
7 - switch (CounterType) {
8 - case PERF_COUNTER_COUNTER:
9 - return "PERF_COUNTER_COUNTER";
10 -
11 - case PERF_COUNTER_TIMER:
12 - return "PERF_COUNTER_TIMER";
13 -
14 - case PERF_COUNTER_QUEUELEN_TYPE:
15 - return "PERF_COUNTER_QUEUELEN_TYPE";
16 -
17 - case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
18 - return "PERF_COUNTER_LARGE_QUEUELEN_TYPE";
19 -
20 - case PERF_COUNTER_100NS_QUEUELEN_TYPE:
21 - return "PERF_COUNTER_100NS_QUEUELEN_TYPE";
22 -
23 - case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
24 - return "PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE";
25 -
26 - case PERF_COUNTER_BULK_COUNT:
27 - return "PERF_COUNTER_BULK_COUNT";
28 -
29 - case PERF_COUNTER_TEXT:
30 - return "PERF_COUNTER_TEXT";
31 -
32 - case PERF_COUNTER_RAWCOUNT:
33 - return "PERF_COUNTER_RAWCOUNT";
34 -
35 - case PERF_COUNTER_LARGE_RAWCOUNT:
36 - return "PERF_COUNTER_LARGE_RAWCOUNT";
37 -
38 - case PERF_COUNTER_RAWCOUNT_HEX:
39 - return "PERF_COUNTER_RAWCOUNT_HEX";
40 -
41 - case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
42 - return "PERF_COUNTER_LARGE_RAWCOUNT_HEX";
43 -
44 - case PERF_SAMPLE_FRACTION:
45 - return "PERF_SAMPLE_FRACTION";
46 -
47 - case PERF_SAMPLE_COUNTER:
48 - return "PERF_SAMPLE_COUNTER";
49 -
50 - case PERF_COUNTER_NODATA:
51 - return "PERF_COUNTER_NODATA";
52 -
53 - case PERF_COUNTER_TIMER_INV:
54 - return "PERF_COUNTER_TIMER_INV";
55 -
56 - case PERF_SAMPLE_BASE:
57 - return "PERF_SAMPLE_BASE";
58 -
59 - case PERF_AVERAGE_TIMER:
60 - return "PERF_AVERAGE_TIMER";
61 -
62 - case PERF_AVERAGE_BASE:
63 - return "PERF_AVERAGE_BASE";
64 -
65 - case PERF_AVERAGE_BULK:
66 - return "PERF_AVERAGE_BULK";
67 -
68 - case PERF_OBJ_TIME_TIMER:
69 - return "PERF_OBJ_TIME_TIMER";
70 -
71 - case PERF_100NSEC_TIMER:
72 - return "PERF_100NSEC_TIMER";
73 -
74 - case PERF_100NSEC_TIMER_INV:
75 - return "PERF_100NSEC_TIMER_INV";
76 -
77 - case PERF_COUNTER_MULTI_TIMER:
78 - return "PERF_COUNTER_MULTI_TIMER";
79 -
80 - case PERF_COUNTER_MULTI_TIMER_INV:
81 - return "PERF_COUNTER_MULTI_TIMER_INV";
82 -
83 - case PERF_COUNTER_MULTI_BASE:
84 - return "PERF_COUNTER_MULTI_BASE";
85 -
86 - case PERF_100NSEC_MULTI_TIMER:
87 - return "PERF_100NSEC_MULTI_TIMER";
88 -
89 - case PERF_100NSEC_MULTI_TIMER_INV:
90 - return "PERF_100NSEC_MULTI_TIMER_INV";
91 -
92 - case PERF_RAW_FRACTION:
93 - return "PERF_RAW_FRACTION";
94 -
95 - case PERF_LARGE_RAW_FRACTION:
96 - return "PERF_LARGE_RAW_FRACTION";
97 -
98 - case PERF_RAW_BASE:
99 - return "PERF_RAW_BASE";
100 -
101 - case PERF_LARGE_RAW_BASE:
102 - return "PERF_LARGE_RAW_BASE";
103 -
104 - case PERF_ELAPSED_TIME:
105 - return "PERF_ELAPSED_TIME";
106 -
107 - case PERF_COUNTER_HISTOGRAM_TYPE:
108 - return "PERF_COUNTER_HISTOGRAM_TYPE";
109 -
110 - case PERF_COUNTER_DELTA:
111 - return "PERF_COUNTER_DELTA";
112 -
113 - case PERF_COUNTER_LARGE_DELTA:
114 - return "PERF_COUNTER_LARGE_DELTA";
115 -
116 - case PERF_PRECISION_SYSTEM_TIMER:
117 - return "PERF_PRECISION_SYSTEM_TIMER";
118 -
119 - case PERF_PRECISION_100NS_TIMER:
120 - return "PERF_PRECISION_100NS_TIMER";
121 -
122 - case PERF_PRECISION_OBJECT_TIMER:
123 - return "PERF_PRECISION_OBJECT_TIMER";
124 -
125 - default:
126 - return "UNKNOWN_COUNTER_TYPE";
127 - }
128 -}
129 -
130 -static const char *getCounterDescription(DWORD CounterType) {
131 - switch (CounterType) {
132 - case PERF_COUNTER_COUNTER:
133 - return "32-bit Counter. Divide delta by delta time. Display suffix: \"/sec\"";
134 -
135 - case PERF_COUNTER_TIMER:
136 - return "64-bit Timer. Divide delta by delta time. Display suffix: \"%\"";
137 -
138 - case PERF_COUNTER_QUEUELEN_TYPE:
139 - case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
140 - return "Queue Length Space-Time Product. Divide delta by delta time. No Display Suffix";
141 -
142 - case PERF_COUNTER_100NS_QUEUELEN_TYPE:
143 - return "Queue Length Space-Time Product using 100 Ns timebase. Divide delta by delta time. No Display Suffix";
144 -
145 - case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
146 - return "Queue Length Space-Time Product using Object specific timebase. Divide delta by delta time. No Display Suffix.";
147 -
148 - case PERF_COUNTER_BULK_COUNT:
149 - return "64-bit Counter. Divide delta by delta time. Display Suffix: \"/sec\"";
150 -
151 - case PERF_COUNTER_TEXT:
152 - return "Unicode text Display as text.";
153 -
154 - case PERF_COUNTER_RAWCOUNT:
155 - case PERF_COUNTER_LARGE_RAWCOUNT:
156 - return "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix.";
157 -
158 - case PERF_COUNTER_RAWCOUNT_HEX:
159 - case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
160 - return "Special case for RAWCOUNT which should be displayed in hex. A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix.";
161 -
162 - case PERF_SAMPLE_FRACTION:
163 - return "A count which is either 1 or 0 on each sampling interrupt (% busy). Divide delta by delta base. Display Suffix: \"%\"";
164 -
165 - case PERF_SAMPLE_COUNTER:
166 - return "A count which is sampled on each sampling interrupt (queue length). Divide delta by delta time. No Display Suffix.";
167 -
168 - case PERF_COUNTER_NODATA:
169 - return "A label: no data is associated with this counter (it has 0 length). Do not display.";
170 -
171 - case PERF_COUNTER_TIMER_INV:
172 - return "64-bit Timer inverse (e.g., idle is measured, but display busy %). Display 100 - delta divided by delta time. Display suffix: \"%\"";
173 -
174 - case PERF_SAMPLE_BASE:
175 - return "The divisor for a sample, used with the previous counter to form a sampled %. You must check for >0 before dividing by this! This counter will directly follow the numerator counter. It should not be displayed to the user.";
176 -
177 - case PERF_AVERAGE_TIMER:
178 - return "A timer which, when divided by an average base, produces a time in seconds which is the average time of some operation. This timer times total operations, and the base is the number of operations. Display Suffix: \"sec\"";
179 -
180 - case PERF_AVERAGE_BASE:
181 - return "Used as the denominator in the computation of time or count averages. Must directly follow the numerator counter. Not displayed to the user.";
182 -
183 - case PERF_AVERAGE_BULK:
184 - return "A bulk count which, when divided (typically) by the number of operations, gives (typically) the number of bytes per operation. No Display Suffix.";
185 -
186 - case PERF_OBJ_TIME_TIMER:
187 - return "64-bit Timer in object specific units. Display delta divided by delta time as returned in the object type header structure. Display suffix: \"%\"";
188 -
189 - case PERF_100NSEC_TIMER:
190 - return "64-bit Timer in 100 nsec units. Display delta divided by delta time. Display suffix: \"%\"";
191 -
192 - case PERF_100NSEC_TIMER_INV:
193 - return "64-bit Timer inverse (e.g., idle is measured, but display busy %). Display 100 - delta divided by delta time. Display suffix: \"%\"";
194 -
195 - case PERF_COUNTER_MULTI_TIMER:
196 - return "64-bit Timer. Divide delta by delta time. Display suffix: \"%\". Timer for multiple instances, so result can exceed 100%.";
197 -
198 - case PERF_COUNTER_MULTI_TIMER_INV:
199 - return "64-bit Timer inverse (e.g., idle is measured, but display busy %). Display 100 * _MULTI_BASE - delta divided by delta time. Display suffix: \"%\" Timer for multiple instances, so result can exceed 100%. Followed by a counter of type _MULTI_BASE.";
200 -
201 - case PERF_COUNTER_MULTI_BASE:
202 - return "Number of instances to which the preceding _MULTI_..._INV counter applies. Used as a factor to get the percentage.";
203 -
204 - case PERF_100NSEC_MULTI_TIMER:
205 - return "64-bit Timer in 100 nsec units. Display delta divided by delta time. Display suffix: \"%\" Timer for multiple instances, so result can exceed 100%.";
206 -
207 - case PERF_100NSEC_MULTI_TIMER_INV:
208 - return "64-bit Timer inverse (e.g., idle is measured, but display busy %). Display 100 * _MULTI_BASE - delta divided by delta time. Display suffix: \"%\" Timer for multiple instances, so result can exceed 100%. Followed by a counter of type _MULTI_BASE.";
209 -
210 - case PERF_LARGE_RAW_FRACTION:
211 - case PERF_RAW_FRACTION:
212 - return "Indicates the data is a fraction of the following counter which should not be time averaged on display (such as free space over total space.) Display as is. Display the quotient as \"%\"";
213 -
214 - case PERF_RAW_BASE:
215 - case PERF_LARGE_RAW_BASE:
216 - return "Indicates the data is a base for the preceding counter which should not be time averaged on display (such as free space over total space.)";
217 -
218 - case PERF_ELAPSED_TIME:
219 - return "The data collected in this counter is actually the start time of the item being measured. For display, this data is subtracted from the sample time to yield the elapsed time as the difference between the two. In the definition below, the PerfTime field of the Object contains the sample time as indicated by the PERF_OBJECT_TIMER bit and the difference is scaled by the PerfFreq of the Object to convert the time units into seconds.";
220 -
221 - case PERF_COUNTER_HISTOGRAM_TYPE:
222 - return "Counter type can be used with the preceding types to define a range of values to be displayed in a histogram.";
223 -
224 - case PERF_COUNTER_DELTA:
225 - case PERF_COUNTER_LARGE_DELTA:
226 - return "This counter is used to display the difference from one sample to the next. The counter value is a constantly increasing number and the value displayed is the difference between the current value and the previous value. Negative numbers are not allowed which shouldn't be a problem as long as the counter value is increasing or unchanged.";
227 -
228 - case PERF_PRECISION_SYSTEM_TIMER:
229 - return "The precision counters are timers that consist of two counter values:\r\n\t1) the count of elapsed time of the event being monitored\r\n\t2) the \"clock\" time in the same units\r\nthe precision timers are used where the standard system timers are not precise enough for accurate readings. It's assumed that the service providing the data is also providing a timestamp at the same time which will eliminate any error that may occur since some small and variable time elapses between the time the system timestamp is captured and when the data is collected from the performance DLL. Only in extreme cases has this been observed to be problematic.\r\nwhen using this type of timer, the definition of the PERF_PRECISION_TIMESTAMP counter must immediately follow the definition of the PERF_PRECISION_*_TIMER in the Object header\r\nThe timer used has the same frequency as the System Performance Timer";
230 -
231 - case PERF_PRECISION_100NS_TIMER:
232 - return "The precision counters are timers that consist of two counter values:\r\n\t1) the count of elapsed time of the event being monitored\r\n\t2) the \"clock\" time in the same units\r\nthe precision timers are used where the standard system timers are not precise enough for accurate readings. It's assumed that the service providing the data is also providing a timestamp at the same time which will eliminate any error that may occur since some small and variable time elapses between the time the system timestamp is captured and when the data is collected from the performance DLL. Only in extreme cases has this been observed to be problematic.\r\nwhen using this type of timer, the definition of the PERF_PRECISION_TIMESTAMP counter must immediately follow the definition of the PERF_PRECISION_*_TIMER in the Object header\r\nThe timer used has the same frequency as the 100 NanoSecond Timer";
233 -
234 - case PERF_PRECISION_OBJECT_TIMER:
235 - return "The precision counters are timers that consist of two counter values:\r\n\t1) the count of elapsed time of the event being monitored\r\n\t2) the \"clock\" time in the same units\r\nthe precision timers are used where the standard system timers are not precise enough for accurate readings. It's assumed that the service providing the data is also providing a timestamp at the same time which will eliminate any error that may occur since some small and variable time elapses between the time the system timestamp is captured and when the data is collected from the performance DLL. Only in extreme cases has this been observed to be problematic.\r\nwhen using this type of timer, the definition of the PERF_PRECISION_TIMESTAMP counter must immediately follow the definition of the PERF_PRECISION_*_TIMER in the Object header\r\nThe timer used is of the frequency specified in the Object header's. PerfFreq field (PerfTime is ignored)";
236 -
237 - default:
238 - return "";
239 - }
240 -}
241 -
242 -static const char *getCounterAlgorithm(DWORD CounterType) {
243 - switch (CounterType)
244 - {
245 - case PERF_COUNTER_COUNTER:
246 - case PERF_SAMPLE_COUNTER:
247 - case PERF_COUNTER_BULK_COUNT:
248 - return "(data1 - data0) / ((time1 - time0) / frequency)";
249 -
250 - case PERF_COUNTER_QUEUELEN_TYPE:
251 - case PERF_COUNTER_100NS_QUEUELEN_TYPE:
252 - case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
253 - case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
254 - case PERF_AVERAGE_BULK: // normally not displayed
255 - return "(data1 - data0) / (time1 - time0)";
256 -
257 - case PERF_OBJ_TIME_TIMER:
258 - case PERF_COUNTER_TIMER:
259 - case PERF_100NSEC_TIMER:
260 - case PERF_PRECISION_SYSTEM_TIMER:
261 - case PERF_PRECISION_100NS_TIMER:
262 - case PERF_PRECISION_OBJECT_TIMER:
263 - case PERF_SAMPLE_FRACTION:
264 - return "100 * (data1 - data0) / (time1 - time0)";
265 -
266 - case PERF_COUNTER_TIMER_INV:
267 - return "100 * (1 - ((data1 - data0) / (time1 - time0)))";
268 -
269 - case PERF_100NSEC_TIMER_INV:
270 - return "100 * (1- (data1 - data0) / (time1 - time0))";
271 -
272 - case PERF_COUNTER_MULTI_TIMER:
273 - return "100 * ((data1 - data0) / ((time1 - time0) / frequency1)) / multi1";
274 -
275 - case PERF_100NSEC_MULTI_TIMER:
276 - return "100 * ((data1 - data0) / (time1 - time0)) / multi1";
277 -
278 - case PERF_COUNTER_MULTI_TIMER_INV:
279 - case PERF_100NSEC_MULTI_TIMER_INV:
280 - return "100 * (multi1 - ((data1 - data0) / (time1 - time0)))";
281 -
282 - case PERF_COUNTER_RAWCOUNT:
283 - case PERF_COUNTER_LARGE_RAWCOUNT:
284 - return "data0";
285 -
286 - case PERF_COUNTER_RAWCOUNT_HEX:
287 - case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
288 - return "hex(data0)";
289 -
290 - case PERF_COUNTER_DELTA:
291 - case PERF_COUNTER_LARGE_DELTA:
292 - return "data1 - data0";
293 -
294 - case PERF_RAW_FRACTION:
295 - case PERF_LARGE_RAW_FRACTION:
296 - return "100 * data0 / time0";
297 -
298 - case PERF_AVERAGE_TIMER:
299 - return "((data1 - data0) / frequency1) / (time1 - time0)";
300 -
301 - case PERF_ELAPSED_TIME:
302 - return "(time0 - data0) / frequency0";
303 -
304 - case PERF_COUNTER_TEXT:
305 - case PERF_SAMPLE_BASE:
306 - case PERF_AVERAGE_BASE:
307 - case PERF_COUNTER_MULTI_BASE:
308 - case PERF_RAW_BASE:
309 - case PERF_COUNTER_NODATA:
310 - case PERF_PRECISION_TIMESTAMP:
311 - default:
312 - return "";
313 - }
314 -}
315 -
316 -void dumpSystemTime(BUFFER *wb, SYSTEMTIME *st) {
317 - buffer_json_member_add_uint64(wb, "Year", st->wYear);
318 - buffer_json_member_add_uint64(wb, "Month", st->wMonth);
319 - buffer_json_member_add_uint64(wb, "DayOfWeek", st->wDayOfWeek);
320 - buffer_json_member_add_uint64(wb, "Day", st->wDay);
321 - buffer_json_member_add_uint64(wb, "Hour", st->wHour);
322 - buffer_json_member_add_uint64(wb, "Minute", st->wMinute);
323 - buffer_json_member_add_uint64(wb, "Second", st->wSecond);
324 - buffer_json_member_add_uint64(wb, "Milliseconds", st->wMilliseconds);
325 -}
326 -
327 -bool dumpDataCb(PERF_DATA_BLOCK *pDataBlock, void *data) {
328 - char name[4096];
329 - if(!getSystemName(pDataBlock, name, sizeof(name)))
330 - strncpyz(name, "[failed]", sizeof(name) - 1);
331 -
332 - BUFFER *wb = data;
333 - buffer_json_member_add_string(wb, "SystemName", name);
334 -
335 - // Number of types of objects being reported
336 - // Type: DWORD
337 - buffer_json_member_add_int64(wb, "NumObjectTypes", pDataBlock->NumObjectTypes);
338 -
339 - buffer_json_member_add_int64(wb, "LittleEndian", pDataBlock->LittleEndian);
340 -
341 - // Version and Revision of these data structures.
342 - // Version starts at 1.
343 - // Revision starts at 0 for each Version.
344 - // Type: DWORD
345 - buffer_json_member_add_int64(wb, "Version", pDataBlock->Version);
346 - buffer_json_member_add_int64(wb, "Revision", pDataBlock->Revision);
347 -
348 - // Object Title Index of default object to display when data from this system is retrieved
349 - // (-1 = none, but this is not expected to be used)
350 - // Type: LONG
351 - buffer_json_member_add_int64(wb, "DefaultObject", pDataBlock->DefaultObject);
352 -
353 - // Performance counter frequency at the system under measurement
354 - // Type: LARGE_INTEGER
355 - buffer_json_member_add_int64(wb, "PerfFreq", pDataBlock->PerfFreq.QuadPart);
356 -
357 - // Performance counter value at the system under measurement
358 - // Type: LARGE_INTEGER
359 - buffer_json_member_add_int64(wb, "PerfTime", pDataBlock->PerfTime.QuadPart);
360 -
361 - // Performance counter time in 100 nsec units at the system under measurement
362 - // Type: LARGE_INTEGER
363 - buffer_json_member_add_int64(wb, "PerfTime100nSec", pDataBlock->PerfTime100nSec.QuadPart);
364 -
365 - // Time at the system under measurement in UTC
366 - // Type: SYSTEMTIME
367 - buffer_json_member_add_object(wb, "SystemTime");
368 - dumpSystemTime(wb, &pDataBlock->SystemTime);
369 - buffer_json_object_close(wb);
370 -
371 - if(pDataBlock->NumObjectTypes)
372 - buffer_json_member_add_array(wb, "Objects");
373 -
374 - return true;
375 -}
376 -
377 -static const char *GetDetailLevel(DWORD num) {
378 - switch (num) {
379 - case 100:
380 - return "Novice (100)";
381 - case 200:
382 - return "Advanced (200)";
383 - case 300:
384 - return "Expert (300)";
385 - case 400:
386 - return "Wizard (400)";
387 -
388 - default:
389 - return "Unknown";
390 - }
391 -}
392 -
393 -bool dumpObjectCb(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, void *data) {
394 - (void)pDataBlock;
395 - BUFFER *wb = data;
396 - if(!pObjectType) {
397 - buffer_json_array_close(wb); // instances or counters
398 - buffer_json_object_close(wb); // objectType
399 - return true;
400 - }
401 -
402 - buffer_json_add_array_item_object(wb); // objectType
403 - buffer_json_member_add_int64(wb, "NameId", pObjectType->ObjectNameTitleIndex);
404 - buffer_json_member_add_string(wb, "Name", RegistryFindNameByID(pObjectType->ObjectNameTitleIndex));
405 - buffer_json_member_add_int64(wb, "HelpId", pObjectType->ObjectHelpTitleIndex);
406 - buffer_json_member_add_string(wb, "Help", RegistryFindHelpByID(pObjectType->ObjectHelpTitleIndex));
407 - buffer_json_member_add_int64(wb, "NumInstances", pObjectType->NumInstances);
408 - buffer_json_member_add_int64(wb, "NumCounters", pObjectType->NumCounters);
409 - buffer_json_member_add_int64(wb, "PerfTime", pObjectType->PerfTime.QuadPart);
410 - buffer_json_member_add_int64(wb, "PerfFreq", pObjectType->PerfFreq.QuadPart);
411 - buffer_json_member_add_int64(wb, "CodePage", pObjectType->CodePage);
412 - buffer_json_member_add_int64(wb, "DefaultCounter", pObjectType->DefaultCounter);
413 - buffer_json_member_add_string(wb, "DetailLevel", GetDetailLevel(pObjectType->DetailLevel));
414 -
415 - if(ObjectTypeHasInstances(pDataBlock, pObjectType))
416 - buffer_json_member_add_array(wb, "Instances");
417 - else
418 - buffer_json_member_add_array(wb, "Counters");
419 -
420 - return true;
421 -}
422 -
423 -bool dumpInstanceCb(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, void *data) {
424 - (void)pDataBlock;
425 - BUFFER *wb = data;
426 - if(!pInstance) {
427 - buffer_json_array_close(wb); // counters
428 - buffer_json_object_close(wb); // instance
429 - return true;
430 - }
431 -
432 - char name[4096];
433 - if(!getInstanceName(pDataBlock, pObjectType, pInstance, name, sizeof(name)))
434 - strncpyz(name, "[failed]", sizeof(name) - 1);
435 -
436 - buffer_json_add_array_item_object(wb);
437 - buffer_json_member_add_string(wb, "Instance", name);
438 - buffer_json_member_add_int64(wb, "UniqueID", pInstance->UniqueID);
439 - buffer_json_member_add_array(wb, "Labels");
440 - {
441 - buffer_json_add_array_item_object(wb);
442 - {
443 - buffer_json_member_add_string(wb, "key", RegistryFindNameByID(pObjectType->ObjectNameTitleIndex));
444 - buffer_json_member_add_string(wb, "value", name);
445 - }
446 - buffer_json_object_close(wb);
447 -
448 - if(pInstance->ParentObjectTitleIndex) {
449 - PERF_INSTANCE_DEFINITION *pi = pInstance;
450 - while(pi->ParentObjectTitleIndex) {
451 - PERF_OBJECT_TYPE *po = getObjectTypeByIndex(pDataBlock, pInstance->ParentObjectTitleIndex);
452 - pi = getInstanceByPosition(pDataBlock, po, pi->ParentObjectInstance);
453 -
454 - if(!getInstanceName(pDataBlock, po, pi, name, sizeof(name)))
455 - strncpyz(name, "[failed]", sizeof(name) - 1);
456 -
457 - buffer_json_add_array_item_object(wb);
458 - {
459 - buffer_json_member_add_string(wb, "key", RegistryFindNameByID(po->ObjectNameTitleIndex));
460 - buffer_json_member_add_string(wb, "value", name);
461 - }
462 - buffer_json_object_close(wb);
463 - }
464 - }
465 - }
466 - buffer_json_array_close(wb); // rrdlabels
467 -
468 - buffer_json_member_add_array(wb, "Counters");
469 - return true;
470 -}
471 -
472 -void dumpSample(BUFFER *wb, RAW_DATA *d) {
473 - buffer_json_member_add_object(wb, "Value");
474 - buffer_json_member_add_uint64(wb, "data", d->Data);
475 - buffer_json_member_add_int64(wb, "time", d->Time);
476 - buffer_json_member_add_uint64(wb, "type", d->CounterType);
477 - buffer_json_member_add_int64(wb, "multi", d->MultiCounterData);
478 - buffer_json_member_add_int64(wb, "frequency", d->Frequency);
479 - buffer_json_object_close(wb);
480 -}
481 -
482 -bool dumpCounterCb(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_COUNTER_DEFINITION *pCounter, RAW_DATA *sample, void *data) {
483 - (void)pDataBlock;
484 - (void)pObjectType;
485 - BUFFER *wb = data;
486 - buffer_json_add_array_item_object(wb);
487 - buffer_json_member_add_string(wb, "Counter", RegistryFindNameByID(pCounter->CounterNameTitleIndex));
488 - dumpSample(wb, sample);
489 - buffer_json_member_add_string(wb, "Help", RegistryFindHelpByID(pCounter->CounterHelpTitleIndex));
490 - buffer_json_member_add_string(wb, "Type", getCounterType(pCounter->CounterType));
491 - buffer_json_member_add_string(wb, "Algorithm", getCounterAlgorithm(pCounter->CounterType));
492 - buffer_json_member_add_string(wb, "Description", getCounterDescription(pCounter->CounterType));
493 - buffer_json_object_close(wb);
494 - return true;
495 -}
496 -
497 -bool dumpInstanceCounterCb(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, PERF_COUNTER_DEFINITION *pCounter, RAW_DATA *sample, void *data) {
498 - (void)pInstance;
499 - return dumpCounterCb(pDataBlock, pObjectType, pCounter, sample, data);
500 -}
501 -
502 -
503 -int windows_perflib_dump(const char *key) {
504 - if(key && !*key)
505 - key = NULL;
506 -
507 - PerflibNamesRegistryInitialize();
508 -
509 - DWORD id = 0;
510 - if(key) {
511 - id = RegistryFindIDByName(key);
512 - if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) {
513 - fprintf(stderr, "Cannot find key '%s' in Windows Performance Counters Registry.\n", key);
514 - exit(1);
515 - }
516 - }
517 -
518 - CLEAN_BUFFER *wb = buffer_create(0, NULL);
519 - buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
520 -
521 - perflibQueryAndTraverse(id, dumpDataCb, dumpObjectCb, dumpInstanceCb, dumpInstanceCounterCb, dumpCounterCb, wb);
522 -
523 - buffer_json_finalize(wb);
524 - printf("\n%s\n", buffer_tostring(wb));
525 -
526 - perflibFreePerformanceData();
527 -
528 - return 0;
529 -}
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "perflib.h"
4 +
5 +#if defined(OS_WINDOWS)
6 +#include <windows.h>
7 +
8 +static const char *getCounterType(DWORD CounterType) {
9 + switch (CounterType) {
10 + case PERF_COUNTER_COUNTER:
11 + return "PERF_COUNTER_COUNTER";
12 +
13 + case PERF_COUNTER_TIMER:
14 + return "PERF_COUNTER_TIMER";
15 +
16 + case PERF_COUNTER_QUEUELEN_TYPE:
17 + return "PERF_COUNTER_QUEUELEN_TYPE";
18 +
19 + case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
20 + return "PERF_COUNTER_LARGE_QUEUELEN_TYPE";
21 +
22 + case PERF_COUNTER_100NS_QUEUELEN_TYPE:
23 + return "PERF_COUNTER_100NS_QUEUELEN_TYPE";
24 +
25 + case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
26 + return "PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE";
27 +
28 + case PERF_COUNTER_BULK_COUNT:
29 + return "PERF_COUNTER_BULK_COUNT";
30 +
31 + case PERF_COUNTER_TEXT:
32 + return "PERF_COUNTER_TEXT";
33 +
34 + case PERF_COUNTER_RAWCOUNT:
35 + return "PERF_COUNTER_RAWCOUNT";
36 +
37 + case PERF_COUNTER_LARGE_RAWCOUNT:
38 + return "PERF_COUNTER_LARGE_RAWCOUNT";
39 +
40 + case PERF_COUNTER_RAWCOUNT_HEX:
41 + return "PERF_COUNTER_RAWCOUNT_HEX";
42 +
43 + case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
44 + return "PERF_COUNTER_LARGE_RAWCOUNT_HEX";
45 +
46 + case PERF_SAMPLE_FRACTION:
47 + return "PERF_SAMPLE_FRACTION";
48 +
49 + case PERF_SAMPLE_COUNTER:
50 + return "PERF_SAMPLE_COUNTER";
51 +
52 + case PERF_COUNTER_NODATA:
53 + return "PERF_COUNTER_NODATA";
54 +
55 + case PERF_COUNTER_TIMER_INV:
56 + return "PERF_COUNTER_TIMER_INV";
57 +
58 + case PERF_SAMPLE_BASE:
59 + return "PERF_SAMPLE_BASE";
60 +
61 + case PERF_AVERAGE_TIMER:
62 + return "PERF_AVERAGE_TIMER";
63 +
64 + case PERF_AVERAGE_BASE:
65 + return "PERF_AVERAGE_BASE";
66 +
67 + case PERF_AVERAGE_BULK:
68 + return "PERF_AVERAGE_BULK";
69 +
70 + case PERF_OBJ_TIME_TIMER:
71 + return "PERF_OBJ_TIME_TIMER";
72 +
73 + case PERF_100NSEC_TIMER:
74 + return "PERF_100NSEC_TIMER";
75 +
76 + case PERF_100NSEC_TIMER_INV:
77 + return "PERF_100NSEC_TIMER_INV";
78 +
79 + case PERF_COUNTER_MULTI_TIMER:
80 + return "PERF_COUNTER_MULTI_TIMER";
81 +
82 + case PERF_COUNTER_MULTI_TIMER_INV:
83 + return "PERF_COUNTER_MULTI_TIMER_INV";
84 +
85 + case PERF_COUNTER_MULTI_BASE:
86 + return "PERF_COUNTER_MULTI_BASE";
87 +
88 + case PERF_100NSEC_MULTI_TIMER:
89 + return "PERF_100NSEC_MULTI_TIMER";
90 +
91 + case PERF_100NSEC_MULTI_TIMER_INV:
92 + return "PERF_100NSEC_MULTI_TIMER_INV";
93 +
94 + case PERF_RAW_FRACTION:
95 + return "PERF_RAW_FRACTION";
96 +
97 + case PERF_LARGE_RAW_FRACTION:
98 + return "PERF_LARGE_RAW_FRACTION";
99 +
100 + case PERF_RAW_BASE:
101 + return "PERF_RAW_BASE";
102 +
103 + case PERF_LARGE_RAW_BASE:
104 + return "PERF_LARGE_RAW_BASE";
105 +
106 + case PERF_ELAPSED_TIME:
107 + return "PERF_ELAPSED_TIME";
108 +
109 + case PERF_COUNTER_HISTOGRAM_TYPE:
110 + return "PERF_COUNTER_HISTOGRAM_TYPE";
111 +
112 + case PERF_COUNTER_DELTA:
113 + return "PERF_COUNTER_DELTA";
114 +
115 + case PERF_COUNTER_LARGE_DELTA:
116 + return "PERF_COUNTER_LARGE_DELTA";
117 +
118 + case PERF_PRECISION_SYSTEM_TIMER:
119 + return "PERF_PRECISION_SYSTEM_TIMER";
120 +
121 + case PERF_PRECISION_100NS_TIMER:
122 + return "PERF_PRECISION_100NS_TIMER";
123 +
124 + case PERF_PRECISION_OBJECT_TIMER:
125 + return "PERF_PRECISION_OBJECT_TIMER";
126 +
127 + default:
128 + return "UNKNOWN_COUNTER_TYPE";
129 + }
130 +}
131 +
132 +static const char *getCounterDescription(DWORD CounterType) {
133 + switch (CounterType) {
134 + case PERF_COUNTER_COUNTER:
135 + return "32-bit Counter. Divide delta by delta time. Display suffix: \"/sec\"";
136 +
137 + case PERF_COUNTER_TIMER:
138 + return "64-bit Timer. Divide delta by delta time. Display suffix: \"%\"";
139 +
140 + case PERF_COUNTER_QUEUELEN_TYPE:
141 + case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
142 + return "Queue Length Space-Time Product. Divide delta by delta time. No Display Suffix";
143 +
144 + case PERF_COUNTER_100NS_QUEUELEN_TYPE:
145 + return "Queue Length Space-Time Product using 100 Ns timebase. Divide delta by delta time. No Display Suffix";
146 +
147 + case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
148 + return "Queue Length Space-Time Product using Object specific timebase. Divide delta by delta time. No Display Suffix.";
149 +
150 + case PERF_COUNTER_BULK_COUNT:
151 + return "64-bit Counter. Divide delta by delta time. Display Suffix: \"/sec\"";
152 +
153 + case PERF_COUNTER_TEXT:
154 + return "Unicode text Display as text.";
155 +
156 + case PERF_COUNTER_RAWCOUNT:
157 + case PERF_COUNTER_LARGE_RAWCOUNT:
158 + return "A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix.";
159 +
160 + case PERF_COUNTER_RAWCOUNT_HEX:
161 + case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
162 + return "Special case for RAWCOUNT which should be displayed in hex. A counter which should not be time averaged on display (such as an error counter on a serial line). Display as is. No Display Suffix.";
163 +
164 + case PERF_SAMPLE_FRACTION:
165 + return "A count which is either 1 or 0 on each sampling interrupt (% busy). Divide delta by delta base. Display Suffix: \"%\"";
166 +
167 + case PERF_SAMPLE_COUNTER:
168 + return "A count which is sampled on each sampling interrupt (queue length). Divide delta by delta time. No Display Suffix.";
169 +
170 + case PERF_COUNTER_NODATA:
171 + return "A label: no data is associated with this counter (it has 0 length). Do not display.";
172 +
173 + case PERF_COUNTER_TIMER_INV:
174 + return "64-bit Timer inverse (e.g., idle is measured, but display busy %). Display 100 - delta divided by delta time. Display suffix: \"%\"";
175 +
176 + case PERF_SAMPLE_BASE:
177 + return "The divisor for a sample, used with the previous counter to form a sampled %. You must check for >0 before dividing by this! This counter will directly follow the numerator counter. It should not be displayed to the user.";
178 +
179 + case PERF_AVERAGE_TIMER:
180 + return "A timer which, when divided by an average base, produces a time in seconds which is the average time of some operation. This timer times total operations, and the base is the number of operations. Display Suffix: \"sec\"";
181 +
182 + case PERF_AVERAGE_BASE:
183 + return "Used as the denominator in the computation of time or count averages. Must directly follow the numerator counter. Not displayed to the user.";
184 +
185 + case PERF_AVERAGE_BULK:
186 + return "A bulk count which, when divided (typically) by the number of operations, gives (typically) the number of bytes per operation. No Display Suffix.";
187 +
188 + case PERF_OBJ_TIME_TIMER:
189 + return "64-bit Timer in object specific units. Display delta divided by delta time as returned in the object type header structure. Display suffix: \"%\"";
190 +
191 + case PERF_100NSEC_TIMER:
192 + return "64-bit Timer in 100 nsec units. Display delta divided by delta time. Display suffix: \"%\"";
193 +
194 + case PERF_100NSEC_TIMER_INV:
195 + return "64-bit Timer inverse (e.g., idle is measured, but display busy %). Display 100 - delta divided by delta time. Display suffix: \"%\"";
196 +
197 + case PERF_COUNTER_MULTI_TIMER:
198 + return "64-bit Timer. Divide delta by delta time. Display suffix: \"%\". Timer for multiple instances, so result can exceed 100%.";
199 +
200 + case PERF_COUNTER_MULTI_TIMER_INV:
201 + return "64-bit Timer inverse (e.g., idle is measured, but display busy %). Display 100 * _MULTI_BASE - delta divided by delta time. Display suffix: \"%\" Timer for multiple instances, so result can exceed 100%. Followed by a counter of type _MULTI_BASE.";
202 +
203 + case PERF_COUNTER_MULTI_BASE:
204 + return "Number of instances to which the preceding _MULTI_..._INV counter applies. Used as a factor to get the percentage.";
205 +
206 + case PERF_100NSEC_MULTI_TIMER:
207 + return "64-bit Timer in 100 nsec units. Display delta divided by delta time. Display suffix: \"%\" Timer for multiple instances, so result can exceed 100%.";
208 +
209 + case PERF_100NSEC_MULTI_TIMER_INV:
210 + return "64-bit Timer inverse (e.g., idle is measured, but display busy %). Display 100 * _MULTI_BASE - delta divided by delta time. Display suffix: \"%\" Timer for multiple instances, so result can exceed 100%. Followed by a counter of type _MULTI_BASE.";
211 +
212 + case PERF_LARGE_RAW_FRACTION:
213 + case PERF_RAW_FRACTION:
214 + return "Indicates the data is a fraction of the following counter which should not be time averaged on display (such as free space over total space.) Display as is. Display the quotient as \"%\"";
215 +
216 + case PERF_RAW_BASE:
217 + case PERF_LARGE_RAW_BASE:
218 + return "Indicates the data is a base for the preceding counter which should not be time averaged on display (such as free space over total space.)";
219 +
220 + case PERF_ELAPSED_TIME:
221 + return "The data collected in this counter is actually the start time of the item being measured. For display, this data is subtracted from the sample time to yield the elapsed time as the difference between the two. In the definition below, the PerfTime field of the Object contains the sample time as indicated by the PERF_OBJECT_TIMER bit and the difference is scaled by the PerfFreq of the Object to convert the time units into seconds.";
222 +
223 + case PERF_COUNTER_HISTOGRAM_TYPE:
224 + return "Counter type can be used with the preceding types to define a range of values to be displayed in a histogram.";
225 +
226 + case PERF_COUNTER_DELTA:
227 + case PERF_COUNTER_LARGE_DELTA:
228 + return "This counter is used to display the difference from one sample to the next. The counter value is a constantly increasing number and the value displayed is the difference between the current value and the previous value. Negative numbers are not allowed which shouldn't be a problem as long as the counter value is increasing or unchanged.";
229 +
230 + case PERF_PRECISION_SYSTEM_TIMER:
231 + return "The precision counters are timers that consist of two counter values:\r\n\t1) the count of elapsed time of the event being monitored\r\n\t2) the \"clock\" time in the same units\r\nthe precision timers are used where the standard system timers are not precise enough for accurate readings. It's assumed that the service providing the data is also providing a timestamp at the same time which will eliminate any error that may occur since some small and variable time elapses between the time the system timestamp is captured and when the data is collected from the performance DLL. Only in extreme cases has this been observed to be problematic.\r\nwhen using this type of timer, the definition of the PERF_PRECISION_TIMESTAMP counter must immediately follow the definition of the PERF_PRECISION_*_TIMER in the Object header\r\nThe timer used has the same frequency as the System Performance Timer";
232 +
233 + case PERF_PRECISION_100NS_TIMER:
234 + return "The precision counters are timers that consist of two counter values:\r\n\t1) the count of elapsed time of the event being monitored\r\n\t2) the \"clock\" time in the same units\r\nthe precision timers are used where the standard system timers are not precise enough for accurate readings. It's assumed that the service providing the data is also providing a timestamp at the same time which will eliminate any error that may occur since some small and variable time elapses between the time the system timestamp is captured and when the data is collected from the performance DLL. Only in extreme cases has this been observed to be problematic.\r\nwhen using this type of timer, the definition of the PERF_PRECISION_TIMESTAMP counter must immediately follow the definition of the PERF_PRECISION_*_TIMER in the Object header\r\nThe timer used has the same frequency as the 100 NanoSecond Timer";
235 +
236 + case PERF_PRECISION_OBJECT_TIMER:
237 + return "The precision counters are timers that consist of two counter values:\r\n\t1) the count of elapsed time of the event being monitored\r\n\t2) the \"clock\" time in the same units\r\nthe precision timers are used where the standard system timers are not precise enough for accurate readings. It's assumed that the service providing the data is also providing a timestamp at the same time which will eliminate any error that may occur since some small and variable time elapses between the time the system timestamp is captured and when the data is collected from the performance DLL. Only in extreme cases has this been observed to be problematic.\r\nwhen using this type of timer, the definition of the PERF_PRECISION_TIMESTAMP counter must immediately follow the definition of the PERF_PRECISION_*_TIMER in the Object header\r\nThe timer used is of the frequency specified in the Object header's. PerfFreq field (PerfTime is ignored)";
238 +
239 + default:
240 + return "";
241 + }
242 +}
243 +
244 +static const char *getCounterAlgorithm(DWORD CounterType) {
245 + switch (CounterType)
246 + {
247 + case PERF_COUNTER_COUNTER:
248 + case PERF_SAMPLE_COUNTER:
249 + case PERF_COUNTER_BULK_COUNT:
250 + return "(data1 - data0) / ((time1 - time0) / frequency)";
251 +
252 + case PERF_COUNTER_QUEUELEN_TYPE:
253 + case PERF_COUNTER_100NS_QUEUELEN_TYPE:
254 + case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
255 + case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
256 + case PERF_AVERAGE_BULK: // normally not displayed
257 + return "(data1 - data0) / (time1 - time0)";
258 +
259 + case PERF_OBJ_TIME_TIMER:
260 + case PERF_COUNTER_TIMER:
261 + case PERF_100NSEC_TIMER:
262 + case PERF_PRECISION_SYSTEM_TIMER:
263 + case PERF_PRECISION_100NS_TIMER:
264 + case PERF_PRECISION_OBJECT_TIMER:
265 + case PERF_SAMPLE_FRACTION:
266 + return "100 * (data1 - data0) / (time1 - time0)";
267 +
268 + case PERF_COUNTER_TIMER_INV:
269 + return "100 * (1 - ((data1 - data0) / (time1 - time0)))";
270 +
271 + case PERF_100NSEC_TIMER_INV:
272 + return "100 * (1- (data1 - data0) / (time1 - time0))";
273 +
274 + case PERF_COUNTER_MULTI_TIMER:
275 + return "100 * ((data1 - data0) / ((time1 - time0) / frequency1)) / multi1";
276 +
277 + case PERF_100NSEC_MULTI_TIMER:
278 + return "100 * ((data1 - data0) / (time1 - time0)) / multi1";
279 +
280 + case PERF_COUNTER_MULTI_TIMER_INV:
281 + case PERF_100NSEC_MULTI_TIMER_INV:
282 + return "100 * (multi1 - ((data1 - data0) / (time1 - time0)))";
283 +
284 + case PERF_COUNTER_RAWCOUNT:
285 + case PERF_COUNTER_LARGE_RAWCOUNT:
286 + return "data0";
287 +
288 + case PERF_COUNTER_RAWCOUNT_HEX:
289 + case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
290 + return "hex(data0)";
291 +
292 + case PERF_COUNTER_DELTA:
293 + case PERF_COUNTER_LARGE_DELTA:
294 + return "data1 - data0";
295 +
296 + case PERF_RAW_FRACTION:
297 + case PERF_LARGE_RAW_FRACTION:
298 + return "100 * data0 / time0";
299 +
300 + case PERF_AVERAGE_TIMER:
301 + return "((data1 - data0) / frequency1) / (time1 - time0)";
302 +
303 + case PERF_ELAPSED_TIME:
304 + return "(time0 - data0) / frequency0";
305 +
306 + case PERF_COUNTER_TEXT:
307 + case PERF_SAMPLE_BASE:
308 + case PERF_AVERAGE_BASE:
309 + case PERF_COUNTER_MULTI_BASE:
310 + case PERF_RAW_BASE:
311 + case PERF_COUNTER_NODATA:
312 + case PERF_PRECISION_TIMESTAMP:
313 + default:
314 + return "";
315 + }
316 +}
317 +
318 +void dumpSystemTime(BUFFER *wb, SYSTEMTIME *st) {
319 + buffer_json_member_add_uint64(wb, "Year", st->wYear);
320 + buffer_json_member_add_uint64(wb, "Month", st->wMonth);
321 + buffer_json_member_add_uint64(wb, "DayOfWeek", st->wDayOfWeek);
322 + buffer_json_member_add_uint64(wb, "Day", st->wDay);
323 + buffer_json_member_add_uint64(wb, "Hour", st->wHour);
324 + buffer_json_member_add_uint64(wb, "Minute", st->wMinute);
325 + buffer_json_member_add_uint64(wb, "Second", st->wSecond);
326 + buffer_json_member_add_uint64(wb, "Milliseconds", st->wMilliseconds);
327 +}
328 +
329 +bool dumpDataCb(PERF_DATA_BLOCK *pDataBlock, void *data) {
330 + char name[4096];
331 + if(!getSystemName(pDataBlock, name, sizeof(name)))
332 + strncpyz(name, "[failed]", sizeof(name) - 1);
333 +
334 + BUFFER *wb = data;
335 + buffer_json_member_add_string(wb, "SystemName", name);
336 +
337 + // Number of types of objects being reported
338 + // Type: DWORD
339 + buffer_json_member_add_int64(wb, "NumObjectTypes", pDataBlock->NumObjectTypes);
340 +
341 + buffer_json_member_add_int64(wb, "LittleEndian", pDataBlock->LittleEndian);
342 +
343 + // Version and Revision of these data structures.
344 + // Version starts at 1.
345 + // Revision starts at 0 for each Version.
346 + // Type: DWORD
347 + buffer_json_member_add_int64(wb, "Version", pDataBlock->Version);
348 + buffer_json_member_add_int64(wb, "Revision", pDataBlock->Revision);
349 +
350 + // Object Title Index of default object to display when data from this system is retrieved
351 + // (-1 = none, but this is not expected to be used)
352 + // Type: LONG
353 + buffer_json_member_add_int64(wb, "DefaultObject", pDataBlock->DefaultObject);
354 +
355 + // Performance counter frequency at the system under measurement
356 + // Type: LARGE_INTEGER
357 + buffer_json_member_add_int64(wb, "PerfFreq", pDataBlock->PerfFreq.QuadPart);
358 +
359 + // Performance counter value at the system under measurement
360 + // Type: LARGE_INTEGER
361 + buffer_json_member_add_int64(wb, "PerfTime", pDataBlock->PerfTime.QuadPart);
362 +
363 + // Performance counter time in 100 nsec units at the system under measurement
364 + // Type: LARGE_INTEGER
365 + buffer_json_member_add_int64(wb, "PerfTime100nSec", pDataBlock->PerfTime100nSec.QuadPart);
366 +
367 + // Time at the system under measurement in UTC
368 + // Type: SYSTEMTIME
369 + buffer_json_member_add_object(wb, "SystemTime");
370 + dumpSystemTime(wb, &pDataBlock->SystemTime);
371 + buffer_json_object_close(wb);
372 +
373 + if(pDataBlock->NumObjectTypes)
374 + buffer_json_member_add_array(wb, "Objects");
375 +
376 + return true;
377 +}
378 +
379 +static const char *GetDetailLevel(DWORD num) {
380 + switch (num) {
381 + case 100:
382 + return "Novice (100)";
383 + case 200:
384 + return "Advanced (200)";
385 + case 300:
386 + return "Expert (300)";
387 + case 400:
388 + return "Wizard (400)";
389 +
390 + default:
391 + return "Unknown";
392 + }
393 +}
394 +
395 +bool dumpObjectCb(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, void *data) {
396 + (void)pDataBlock;
397 + BUFFER *wb = data;
398 + if(!pObjectType) {
399 + buffer_json_array_close(wb); // instances or counters
400 + buffer_json_object_close(wb); // objectType
401 + return true;
402 + }
403 +
404 + buffer_json_add_array_item_object(wb); // objectType
405 + buffer_json_member_add_int64(wb, "NameId", pObjectType->ObjectNameTitleIndex);
406 + buffer_json_member_add_string(wb, "Name", RegistryFindNameByID(pObjectType->ObjectNameTitleIndex));
407 + buffer_json_member_add_int64(wb, "HelpId", pObjectType->ObjectHelpTitleIndex);
408 + buffer_json_member_add_string(wb, "Help", RegistryFindHelpByID(pObjectType->ObjectHelpTitleIndex));
409 + buffer_json_member_add_int64(wb, "NumInstances", pObjectType->NumInstances);
410 + buffer_json_member_add_int64(wb, "NumCounters", pObjectType->NumCounters);
411 + buffer_json_member_add_int64(wb, "PerfTime", pObjectType->PerfTime.QuadPart);
412 + buffer_json_member_add_int64(wb, "PerfFreq", pObjectType->PerfFreq.QuadPart);
413 + buffer_json_member_add_int64(wb, "CodePage", pObjectType->CodePage);
414 + buffer_json_member_add_int64(wb, "DefaultCounter", pObjectType->DefaultCounter);
415 + buffer_json_member_add_string(wb, "DetailLevel", GetDetailLevel(pObjectType->DetailLevel));
416 +
417 + if(ObjectTypeHasInstances(pDataBlock, pObjectType))
418 + buffer_json_member_add_array(wb, "Instances");
419 + else
420 + buffer_json_member_add_array(wb, "Counters");
421 +
422 + return true;
423 +}
424 +
425 +bool dumpInstanceCb(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, void *data) {
426 + (void)pDataBlock;
427 + BUFFER *wb = data;
428 + if(!pInstance) {
429 + buffer_json_array_close(wb); // counters
430 + buffer_json_object_close(wb); // instance
431 + return true;
432 + }
433 +
434 + char name[4096];
435 + if(!getInstanceName(pDataBlock, pObjectType, pInstance, name, sizeof(name)))
436 + strncpyz(name, "[failed]", sizeof(name) - 1);
437 +
438 + buffer_json_add_array_item_object(wb);
439 + buffer_json_member_add_string(wb, "Instance", name);
440 + buffer_json_member_add_int64(wb, "UniqueID", pInstance->UniqueID);
441 + buffer_json_member_add_array(wb, "Labels");
442 + {
443 + buffer_json_add_array_item_object(wb);
444 + {
445 + buffer_json_member_add_string(wb, "key", RegistryFindNameByID(pObjectType->ObjectNameTitleIndex));
446 + buffer_json_member_add_string(wb, "value", name);
447 + }
448 + buffer_json_object_close(wb);
449 +
450 + if(pInstance->ParentObjectTitleIndex) {
451 + PERF_INSTANCE_DEFINITION *pi = pInstance;
452 + while(pi->ParentObjectTitleIndex) {
453 + PERF_OBJECT_TYPE *po = getObjectTypeByIndex(pDataBlock, pInstance->ParentObjectTitleIndex);
454 + pi = getInstanceByPosition(pDataBlock, po, pi->ParentObjectInstance);
455 +
456 + if(!getInstanceName(pDataBlock, po, pi, name, sizeof(name)))
457 + strncpyz(name, "[failed]", sizeof(name) - 1);
458 +
459 + buffer_json_add_array_item_object(wb);
460 + {
461 + buffer_json_member_add_string(wb, "key", RegistryFindNameByID(po->ObjectNameTitleIndex));
462 + buffer_json_member_add_string(wb, "value", name);
463 + }
464 + buffer_json_object_close(wb);
465 + }
466 + }
467 + }
468 + buffer_json_array_close(wb); // rrdlabels
469 +
470 + buffer_json_member_add_array(wb, "Counters");
471 + return true;
472 +}
473 +
474 +void dumpSample(BUFFER *wb, RAW_DATA *d) {
475 + buffer_json_member_add_object(wb, "Value");
476 + buffer_json_member_add_uint64(wb, "data", d->Data);
477 + buffer_json_member_add_int64(wb, "time", d->Time);
478 + buffer_json_member_add_uint64(wb, "type", d->CounterType);
479 + buffer_json_member_add_int64(wb, "multi", d->MultiCounterData);
480 + buffer_json_member_add_int64(wb, "frequency", d->Frequency);
481 + buffer_json_object_close(wb);
482 +}
483 +
484 +bool dumpCounterCb(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_COUNTER_DEFINITION *pCounter, RAW_DATA *sample, void *data) {
485 + (void)pDataBlock;
486 + (void)pObjectType;
487 + BUFFER *wb = data;
488 + buffer_json_add_array_item_object(wb);
489 + buffer_json_member_add_string(wb, "Counter", RegistryFindNameByID(pCounter->CounterNameTitleIndex));
490 + dumpSample(wb, sample);
491 + buffer_json_member_add_string(wb, "Help", RegistryFindHelpByID(pCounter->CounterHelpTitleIndex));
492 + buffer_json_member_add_string(wb, "Type", getCounterType(pCounter->CounterType));
493 + buffer_json_member_add_string(wb, "Algorithm", getCounterAlgorithm(pCounter->CounterType));
494 + buffer_json_member_add_string(wb, "Description", getCounterDescription(pCounter->CounterType));
495 + buffer_json_object_close(wb);
496 + return true;
497 +}
498 +
499 +bool dumpInstanceCounterCb(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, PERF_COUNTER_DEFINITION *pCounter, RAW_DATA *sample, void *data) {
500 + (void)pInstance;
501 + return dumpCounterCb(pDataBlock, pObjectType, pCounter, sample, data);
502 +}
503 +
504 +
505 +int windows_perflib_dump(const char *key) {
506 + if(key && !*key)
507 + key = NULL;
508 +
509 + PerflibNamesRegistryInitialize();
510 +
511 + DWORD id = 0;
512 + if(key) {
513 + id = RegistryFindIDByName(key);
514 + if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) {
515 + fprintf(stderr, "Cannot find key '%s' in Windows Performance Counters Registry.\n", key);
516 + exit(1);
517 + }
518 + }
519 +
520 + CLEAN_BUFFER *wb = buffer_create(0, NULL);
521 + buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
522 +
523 + perflibQueryAndTraverse(id, dumpDataCb, dumpObjectCb, dumpInstanceCb, dumpInstanceCounterCb, dumpCounterCb, wb);
524 +
525 + buffer_json_finalize(wb);
526 + printf("\n%s\n", buffer_tostring(wb));
527 +
528 + perflibFreePerformanceData();
529 +
530 + return 0;
531 +}
532 +
533 +#endif // OS_WINDOWS
\ No newline at end of file
src/libnetdata/os/windows-perflib/perflib-names.c renamed
+247 -242
@@ -1,242 +1,247 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "perflib.h"
4 -
5 -#define REGISTRY_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009"
6 -
7 -typedef struct perflib_registry {
8 - DWORD id;
9 - char *key;
10 - char *help;
11 -} perfLibRegistryEntry;
12 -
13 -static inline bool compare_perfLibRegistryEntry(const char *k1, const char *k2) {
14 - return strcmp(k1, k2) == 0;
15 -}
16 -
17 -static inline const char *value2key_perfLibRegistryEntry(perfLibRegistryEntry *entry) {
18 - return entry->key;
19 -}
20 -
21 -#define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION compare_perfLibRegistryEntry
22 -#define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION value2key_perfLibRegistryEntry
23 -#define SIMPLE_HASHTABLE_KEY_TYPE const char
24 -#define SIMPLE_HASHTABLE_VALUE_TYPE perfLibRegistryEntry
25 -#define SIMPLE_HASHTABLE_NAME _PERFLIB
26 -#include "libnetdata/simple_hashtable.h"
27 -
28 -static struct {
29 - SPINLOCK spinlock;
30 - size_t size;
31 - perfLibRegistryEntry **array;
32 - struct simple_hashtable_PERFLIB hashtable;
33 - FILETIME lastWriteTime;
34 -} names_globals = {
35 - .spinlock = NETDATA_SPINLOCK_INITIALIZER,
36 - .size = 0,
37 - .array = NULL,
38 -};
39 -
40 -DWORD RegistryFindIDByName(const char *name) {
41 - DWORD rc = PERFLIB_REGISTRY_NAME_NOT_FOUND;
42 -
43 - spinlock_lock(&names_globals.spinlock);
44 - XXH64_hash_t hash = XXH3_64bits((void *)name, strlen(name));
45 - SIMPLE_HASHTABLE_SLOT_PERFLIB *sl = simple_hashtable_get_slot_PERFLIB(&names_globals.hashtable, hash, name, false);
46 - perfLibRegistryEntry *e = SIMPLE_HASHTABLE_SLOT_DATA(sl);
47 - if(e) rc = e->id;
48 - spinlock_unlock(&names_globals.spinlock);
49 -
50 - return rc;
51 -}
52 -
53 -static inline void RegistryAddToHashTable_unsafe(perfLibRegistryEntry *entry) {
54 - XXH64_hash_t hash = XXH3_64bits((void *)entry->key, strlen(entry->key));
55 - SIMPLE_HASHTABLE_SLOT_PERFLIB *sl = simple_hashtable_get_slot_PERFLIB(&names_globals.hashtable, hash, entry->key, true);
56 - perfLibRegistryEntry *e = SIMPLE_HASHTABLE_SLOT_DATA(sl);
57 - if(!e || e->id > entry->id)
58 - simple_hashtable_set_slot_PERFLIB(&names_globals.hashtable, sl, hash, entry);
59 -}
60 -
61 -static void RegistrySetData_unsafe(DWORD id, const char *key, const char *help) {
62 - if(id >= names_globals.size) {
63 - // increase the size of the array
64 -
65 - size_t old_size = names_globals.size;
66 -
67 - if(!names_globals.size)
68 - names_globals.size = 20000;
69 - else
70 - names_globals.size *= 2;
71 -
72 - names_globals.array = reallocz(names_globals.array, names_globals.size * sizeof(perfLibRegistryEntry *));
73 -
74 - memset(names_globals.array + old_size, 0, (names_globals.size - old_size) * sizeof(perfLibRegistryEntry *));
75 - }
76 -
77 - perfLibRegistryEntry *entry = names_globals.array[id];
78 - if(!entry)
79 - entry = names_globals.array[id] = (perfLibRegistryEntry *)calloc(1, sizeof(perfLibRegistryEntry));
80 -
81 - bool add_to_hash = false;
82 - if(key && !entry->key) {
83 - entry->key = strdup(key);
84 - add_to_hash = true;
85 - }
86 -
87 - if(help && !entry->help)
88 - entry->help = strdup(help);
89 -
90 - entry->id = id;
91 -
92 - if(add_to_hash)
93 - RegistryAddToHashTable_unsafe(entry);
94 -}
95 -
96 -const char *RegistryFindNameByID(DWORD id) {
97 - const char *s = "";
98 - spinlock_lock(&names_globals.spinlock);
99 -
100 - if(id < names_globals.size) {
101 - perfLibRegistryEntry *titleEntry = names_globals.array[id];
102 - if(titleEntry && titleEntry->key)
103 - s = titleEntry->key;
104 - }
105 -
106 - spinlock_unlock(&names_globals.spinlock);
107 - return s;
108 -}
109 -
110 -const char *RegistryFindHelpByID(DWORD id) {
111 - const char *s = "";
112 - spinlock_lock(&names_globals.spinlock);
113 -
114 - if(id < names_globals.size) {
115 - perfLibRegistryEntry *titleEntry = names_globals.array[id];
116 - if(titleEntry && titleEntry->help)
117 - s = titleEntry->help;
118 - }
119 -
120 - spinlock_unlock(&names_globals.spinlock);
121 - return s;
122 -}
123 -
124 -// ----------------------------------------------------------
125 -
126 -static inline void readRegistryKeys_unsafe(BOOL helps) {
127 - TCHAR *pData = NULL;
128 -
129 - HKEY hKey;
130 - DWORD dwType;
131 - DWORD dwSize = 0;
132 - LONG lStatus;
133 -
134 - LPCSTR valueName;
135 - if(helps)
136 - valueName = TEXT("help");
137 - else
138 - valueName = TEXT("CounterDefinition");
139 -
140 - // Open the key for the English counters
141 - lStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(REGISTRY_KEY), 0, KEY_READ, &hKey);
142 - if (lStatus != ERROR_SUCCESS) {
143 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
144 - "Failed to open registry key HKEY_LOCAL_MACHINE, subkey '%s', error %ld\n", REGISTRY_KEY, (long)lStatus);
145 - return;
146 - }
147 -
148 - // Get the size of the 'Counters' data
149 - lStatus = RegQueryValueEx(hKey, valueName, NULL, &dwType, NULL, &dwSize);
150 - if (lStatus != ERROR_SUCCESS) {
151 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
152 - "Failed to get registry key HKEY_LOCAL_MACHINE, subkey '%s', value '%s', size of data, error %ld\n",
153 - REGISTRY_KEY, (const char *)valueName, (long)lStatus);
154 - goto cleanup;
155 - }
156 -
157 - // Allocate memory for the data
158 - pData = mallocz(dwSize);
159 -
160 - // Read the 'Counters' data
161 - lStatus = RegQueryValueEx(hKey, valueName, NULL, &dwType, (LPBYTE)pData, &dwSize);
162 - if (lStatus != ERROR_SUCCESS || dwType != REG_MULTI_SZ) {
163 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
164 - "Failed to get registry key HKEY_LOCAL_MACHINE, subkey '%s', value '%s', data, error %ld\n",
165 - REGISTRY_KEY, (const char *)valueName, (long)lStatus);
166 - goto cleanup;
167 - }
168 -
169 - // Process the counter data
170 - TCHAR *ptr = pData;
171 - while (*ptr) {
172 - TCHAR *sid = ptr; // First string is the ID
173 - ptr += lstrlen(ptr) + 1; // Move to the next string
174 - TCHAR *name = ptr; // Second string is the name
175 - ptr += lstrlen(ptr) + 1; // Move to the next pair
176 -
177 - DWORD id = strtoul(sid, NULL, 10);
178 -
179 - if(helps)
180 - RegistrySetData_unsafe(id, NULL, name);
181 - else
182 - RegistrySetData_unsafe(id, name, NULL);
183 - }
184 -
185 -cleanup:
186 - if(pData) freez(pData);
187 - RegCloseKey(hKey);
188 -}
189 -
190 -static BOOL RegistryKeyModification(FILETIME *lastWriteTime) {
191 - HKEY hKey;
192 - LONG lResult;
193 - BOOL ret = FALSE;
194 -
195 - // Open the registry key
196 - lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(REGISTRY_KEY), 0, KEY_READ, &hKey);
197 - if (lResult != ERROR_SUCCESS) {
198 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
199 - "Failed to open registry key HKEY_LOCAL_MACHINE, subkey '%s', error %ld\n", REGISTRY_KEY, (long)lResult);
200 - return FALSE;
201 - }
202 -
203 - // Get the last write time
204 - lResult = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, lastWriteTime);
205 - if (lResult != ERROR_SUCCESS) {
206 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
207 - "Failed to query registry key HKEY_LOCAL_MACHINE, subkey '%s', last write time, error %ld\n", REGISTRY_KEY, (long)lResult);
208 - ret = FALSE;
209 - }
210 - else
211 - ret = TRUE;
212 -
213 - RegCloseKey(hKey);
214 - return ret;
215 -}
216 -
217 -static inline void RegistryFetchAll_unsafe(void) {
218 - readRegistryKeys_unsafe(FALSE);
219 - readRegistryKeys_unsafe(TRUE);
220 -}
221 -
222 -void PerflibNamesRegistryInitialize(void) {
223 - spinlock_lock(&names_globals.spinlock);
224 - simple_hashtable_init_PERFLIB(&names_globals.hashtable, 20000);
225 - RegistryKeyModification(&names_globals.lastWriteTime);
226 - RegistryFetchAll_unsafe();
227 - spinlock_unlock(&names_globals.spinlock);
228 -}
229 -
230 -void PerflibNamesRegistryUpdate(void) {
231 - FILETIME lastWriteTime = { 0 };
232 - RegistryKeyModification(&lastWriteTime);
233 -
234 - if(CompareFileTime(&lastWriteTime, &names_globals.lastWriteTime) > 0) {
235 - spinlock_lock(&names_globals.spinlock);
236 - if(CompareFileTime(&lastWriteTime, &names_globals.lastWriteTime) > 0) {
237 - names_globals.lastWriteTime = lastWriteTime;
238 - RegistryFetchAll_unsafe();
239 - }
240 - spinlock_unlock(&names_globals.spinlock);
241 - }
242 -}
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "perflib.h"
4 +
5 +#if defined(OS_WINDOWS)
6 +#include <windows.h>
7 +
8 +#define REGISTRY_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009"
9 +
10 +typedef struct perflib_registry {
11 + DWORD id;
12 + char *key;
13 + char *help;
14 +} perfLibRegistryEntry;
15 +
16 +static inline bool compare_perfLibRegistryEntry(const char *k1, const char *k2) {
17 + return strcmp(k1, k2) == 0;
18 +}
19 +
20 +static inline const char *value2key_perfLibRegistryEntry(perfLibRegistryEntry *entry) {
21 + return entry->key;
22 +}
23 +
24 +#define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION compare_perfLibRegistryEntry
25 +#define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION value2key_perfLibRegistryEntry
26 +#define SIMPLE_HASHTABLE_KEY_TYPE const char
27 +#define SIMPLE_HASHTABLE_VALUE_TYPE perfLibRegistryEntry
28 +#define SIMPLE_HASHTABLE_NAME _PERFLIB
29 +#include "libnetdata/simple_hashtable.h"
30 +
31 +static struct {
32 + SPINLOCK spinlock;
33 + size_t size;
34 + perfLibRegistryEntry **array;
35 + struct simple_hashtable_PERFLIB hashtable;
36 + FILETIME lastWriteTime;
37 +} names_globals = {
38 + .spinlock = NETDATA_SPINLOCK_INITIALIZER,
39 + .size = 0,
40 + .array = NULL,
41 +};
42 +
43 +DWORD RegistryFindIDByName(const char *name) {
44 + DWORD rc = PERFLIB_REGISTRY_NAME_NOT_FOUND;
45 +
46 + spinlock_lock(&names_globals.spinlock);
47 + XXH64_hash_t hash = XXH3_64bits((void *)name, strlen(name));
48 + SIMPLE_HASHTABLE_SLOT_PERFLIB *sl = simple_hashtable_get_slot_PERFLIB(&names_globals.hashtable, hash, name, false);
49 + perfLibRegistryEntry *e = SIMPLE_HASHTABLE_SLOT_DATA(sl);
50 + if(e) rc = e->id;
51 + spinlock_unlock(&names_globals.spinlock);
52 +
53 + return rc;
54 +}
55 +
56 +static inline void RegistryAddToHashTable_unsafe(perfLibRegistryEntry *entry) {
57 + XXH64_hash_t hash = XXH3_64bits((void *)entry->key, strlen(entry->key));
58 + SIMPLE_HASHTABLE_SLOT_PERFLIB *sl = simple_hashtable_get_slot_PERFLIB(&names_globals.hashtable, hash, entry->key, true);
59 + perfLibRegistryEntry *e = SIMPLE_HASHTABLE_SLOT_DATA(sl);
60 + if(!e || e->id > entry->id)
61 + simple_hashtable_set_slot_PERFLIB(&names_globals.hashtable, sl, hash, entry);
62 +}
63 +
64 +static void RegistrySetData_unsafe(DWORD id, const char *key, const char *help) {
65 + if(id >= names_globals.size) {
66 + // increase the size of the array
67 +
68 + size_t old_size = names_globals.size;
69 +
70 + if(!names_globals.size)
71 + names_globals.size = 20000;
72 + else
73 + names_globals.size *= 2;
74 +
75 + names_globals.array = reallocz(names_globals.array, names_globals.size * sizeof(perfLibRegistryEntry *));
76 +
77 + memset(names_globals.array + old_size, 0, (names_globals.size - old_size) * sizeof(perfLibRegistryEntry *));
78 + }
79 +
80 + perfLibRegistryEntry *entry = names_globals.array[id];
81 + if(!entry)
82 + entry = names_globals.array[id] = (perfLibRegistryEntry *)calloc(1, sizeof(perfLibRegistryEntry));
83 +
84 + bool add_to_hash = false;
85 + if(key && !entry->key) {
86 + entry->key = strdup(key);
87 + add_to_hash = true;
88 + }
89 +
90 + if(help && !entry->help)
91 + entry->help = strdup(help);
92 +
93 + entry->id = id;
94 +
95 + if(add_to_hash)
96 + RegistryAddToHashTable_unsafe(entry);
97 +}
98 +
99 +const char *RegistryFindNameByID(DWORD id) {
100 + const char *s = "";
101 + spinlock_lock(&names_globals.spinlock);
102 +
103 + if(id < names_globals.size) {
104 + perfLibRegistryEntry *titleEntry = names_globals.array[id];
105 + if(titleEntry && titleEntry->key)
106 + s = titleEntry->key;
107 + }
108 +
109 + spinlock_unlock(&names_globals.spinlock);
110 + return s;
111 +}
112 +
113 +const char *RegistryFindHelpByID(DWORD id) {
114 + const char *s = "";
115 + spinlock_lock(&names_globals.spinlock);
116 +
117 + if(id < names_globals.size) {
118 + perfLibRegistryEntry *titleEntry = names_globals.array[id];
119 + if(titleEntry && titleEntry->help)
120 + s = titleEntry->help;
121 + }
122 +
123 + spinlock_unlock(&names_globals.spinlock);
124 + return s;
125 +}
126 +
127 +// ----------------------------------------------------------
128 +
129 +static inline void readRegistryKeys_unsafe(BOOL helps) {
130 + TCHAR *pData = NULL;
131 +
132 + HKEY hKey;
133 + DWORD dwType;
134 + DWORD dwSize = 0;
135 + LONG lStatus;
136 +
137 + LPCSTR valueName;
138 + if(helps)
139 + valueName = TEXT("help");
140 + else
141 + valueName = TEXT("CounterDefinition");
142 +
143 + // Open the key for the English counters
144 + lStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(REGISTRY_KEY), 0, KEY_READ, &hKey);
145 + if (lStatus != ERROR_SUCCESS) {
146 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
147 + "Failed to open registry key HKEY_LOCAL_MACHINE, subkey '%s', error %ld\n", REGISTRY_KEY, (long)lStatus);
148 + return;
149 + }
150 +
151 + // Get the size of the 'Counters' data
152 + lStatus = RegQueryValueEx(hKey, valueName, NULL, &dwType, NULL, &dwSize);
153 + if (lStatus != ERROR_SUCCESS) {
154 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
155 + "Failed to get registry key HKEY_LOCAL_MACHINE, subkey '%s', value '%s', size of data, error %ld\n",
156 + REGISTRY_KEY, (const char *)valueName, (long)lStatus);
157 + goto cleanup;
158 + }
159 +
160 + // Allocate memory for the data
161 + pData = mallocz(dwSize);
162 +
163 + // Read the 'Counters' data
164 + lStatus = RegQueryValueEx(hKey, valueName, NULL, &dwType, (LPBYTE)pData, &dwSize);
165 + if (lStatus != ERROR_SUCCESS || dwType != REG_MULTI_SZ) {
166 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
167 + "Failed to get registry key HKEY_LOCAL_MACHINE, subkey '%s', value '%s', data, error %ld\n",
168 + REGISTRY_KEY, (const char *)valueName, (long)lStatus);
169 + goto cleanup;
170 + }
171 +
172 + // Process the counter data
173 + TCHAR *ptr = pData;
174 + while (*ptr) {
175 + TCHAR *sid = ptr; // First string is the ID
176 + ptr += lstrlen(ptr) + 1; // Move to the next string
177 + TCHAR *name = ptr; // Second string is the name
178 + ptr += lstrlen(ptr) + 1; // Move to the next pair
179 +
180 + DWORD id = strtoul(sid, NULL, 10);
181 +
182 + if(helps)
183 + RegistrySetData_unsafe(id, NULL, name);
184 + else
185 + RegistrySetData_unsafe(id, name, NULL);
186 + }
187 +
188 +cleanup:
189 + if(pData) freez(pData);
190 + RegCloseKey(hKey);
191 +}
192 +
193 +static BOOL RegistryKeyModification(FILETIME *lastWriteTime) {
194 + HKEY hKey;
195 + LONG lResult;
196 + BOOL ret = FALSE;
197 +
198 + // Open the registry key
199 + lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(REGISTRY_KEY), 0, KEY_READ, &hKey);
200 + if (lResult != ERROR_SUCCESS) {
201 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
202 + "Failed to open registry key HKEY_LOCAL_MACHINE, subkey '%s', error %ld\n", REGISTRY_KEY, (long)lResult);
203 + return FALSE;
204 + }
205 +
206 + // Get the last write time
207 + lResult = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, lastWriteTime);
208 + if (lResult != ERROR_SUCCESS) {
209 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
210 + "Failed to query registry key HKEY_LOCAL_MACHINE, subkey '%s', last write time, error %ld\n", REGISTRY_KEY, (long)lResult);
211 + ret = FALSE;
212 + }
213 + else
214 + ret = TRUE;
215 +
216 + RegCloseKey(hKey);
217 + return ret;
218 +}
219 +
220 +static inline void RegistryFetchAll_unsafe(void) {
221 + readRegistryKeys_unsafe(FALSE);
222 + readRegistryKeys_unsafe(TRUE);
223 +}
224 +
225 +void PerflibNamesRegistryInitialize(void) {
226 + spinlock_lock(&names_globals.spinlock);
227 + simple_hashtable_init_PERFLIB(&names_globals.hashtable, 20000);
228 + RegistryKeyModification(&names_globals.lastWriteTime);
229 + RegistryFetchAll_unsafe();
230 + spinlock_unlock(&names_globals.spinlock);
231 +}
232 +
233 +void PerflibNamesRegistryUpdate(void) {
234 + FILETIME lastWriteTime = { 0 };
235 + RegistryKeyModification(&lastWriteTime);
236 +
237 + if(CompareFileTime(&lastWriteTime, &names_globals.lastWriteTime) > 0) {
238 + spinlock_lock(&names_globals.spinlock);
239 + if(CompareFileTime(&lastWriteTime, &names_globals.lastWriteTime) > 0) {
240 + names_globals.lastWriteTime = lastWriteTime;
241 + RegistryFetchAll_unsafe();
242 + }
243 + spinlock_unlock(&names_globals.spinlock);
244 + }
245 +}
246 +
247 +#endif // OS_WINDOWS
src/libnetdata/os/windows-perflib/perflib.c renamed
+676 -671
@@ -1,671 +1,676 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "perflib.h"
4 -
5 -// --------------------------------------------------------------------------------
6 -
7 -// Retrieve a buffer that contains the specified performance data.
8 -// The pwszSource parameter determines the data that GetRegistryBuffer returns.
9 -//
10 -// Typically, when calling RegQueryValueEx, you can specify zero for the size of the buffer
11 -// and the RegQueryValueEx will set your size variable to the required buffer size. However,
12 -// if the source is "Global" or one or more object index values, you will need to increment
13 -// the buffer size in a loop until RegQueryValueEx does not return ERROR_MORE_DATA.
14 -static LPBYTE getPerformanceData(const char *pwszSource) {
15 - static __thread DWORD size = 0;
16 - static __thread LPBYTE buffer = NULL;
17 -
18 - if(pwszSource == (const char *)0x01) {
19 - freez(buffer);
20 - buffer = NULL;
21 - size = 0;
22 - return NULL;
23 - }
24 -
25 - if(!size) {
26 - size = 32 * 1024;
27 - buffer = mallocz(size);
28 - }
29 -
30 - LONG status = ERROR_SUCCESS;
31 - while ((status = RegQueryValueEx(HKEY_PERFORMANCE_DATA, pwszSource,
32 - NULL, NULL, buffer, &size)) == ERROR_MORE_DATA) {
33 - size *= 2;
34 - buffer = reallocz(buffer, size);
35 - }
36 -
37 - if (status != ERROR_SUCCESS) {
38 - nd_log(NDLS_COLLECTORS, NDLP_ERR, "RegQueryValueEx failed with 0x%x.\n", status);
39 - return NULL;
40 - }
41 -
42 - return buffer;
43 -}
44 -
45 -void perflibFreePerformanceData(void) {
46 - getPerformanceData((const char *)0x01);
47 -}
48 -
49 -// --------------------------------------------------------------------------------------------------------------------
50 -
51 -// Retrieve the raw counter value and any supporting data needed to calculate
52 -// a displayable counter value. Use the counter type to determine the information
53 -// needed to calculate the value.
54 -
55 -static BOOL getCounterData(
56 - PERF_DATA_BLOCK *pDataBlock,
57 - PERF_OBJECT_TYPE* pObject,
58 - PERF_COUNTER_DEFINITION* pCounter,
59 - PERF_COUNTER_BLOCK* pCounterDataBlock,
60 - PRAW_DATA pRawData)
61 -{
62 - PVOID pData = NULL;
63 - UNALIGNED ULONGLONG* pullData = NULL;
64 - PERF_COUNTER_DEFINITION* pBaseCounter = NULL;
65 - BOOL fSuccess = TRUE;
66 -
67 - //Point to the raw counter data.
68 - pData = (PVOID)((LPBYTE)pCounterDataBlock + pCounter->CounterOffset);
69 -
70 - //Now use the PERF_COUNTER_DEFINITION.CounterType value to figure out what
71 - //other information you need to calculate a displayable value.
72 - switch (pCounter->CounterType) {
73 -
74 - case PERF_COUNTER_COUNTER:
75 - case PERF_COUNTER_QUEUELEN_TYPE:
76 - case PERF_SAMPLE_COUNTER:
77 - pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
78 - pRawData->Time = pDataBlock->PerfTime.QuadPart;
79 - if (PERF_COUNTER_COUNTER == pCounter->CounterType || PERF_SAMPLE_COUNTER == pCounter->CounterType)
80 - pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
81 - break;
82 -
83 - case PERF_OBJ_TIME_TIMER:
84 - pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
85 - pRawData->Time = pObject->PerfTime.QuadPart;
86 - break;
87 -
88 - case PERF_COUNTER_100NS_QUEUELEN_TYPE:
89 - pRawData->Data = *(UNALIGNED ULONGLONG *)pData;
90 - pRawData->Time = pDataBlock->PerfTime100nSec.QuadPart;
91 - break;
92 -
93 - case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
94 - pRawData->Data = *(UNALIGNED ULONGLONG *)pData;
95 - pRawData->Time = pObject->PerfTime.QuadPart;
96 - break;
97 -
98 - case PERF_COUNTER_TIMER:
99 - case PERF_COUNTER_TIMER_INV:
100 - case PERF_COUNTER_BULK_COUNT:
101 - case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
102 - pullData = (UNALIGNED ULONGLONG *)pData;
103 - pRawData->Data = *pullData;
104 - pRawData->Time = pDataBlock->PerfTime.QuadPart;
105 - if (pCounter->CounterType == PERF_COUNTER_BULK_COUNT)
106 - pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
107 - break;
108 -
109 - case PERF_COUNTER_MULTI_TIMER:
110 - case PERF_COUNTER_MULTI_TIMER_INV:
111 - pullData = (UNALIGNED ULONGLONG *)pData;
112 - pRawData->Data = *pullData;
113 - pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
114 - pRawData->Time = pDataBlock->PerfTime.QuadPart;
115 -
116 - //These counter types have a second counter value that is adjacent to
117 - //this counter value in the counter data block. The value is needed for
118 - //the calculation.
119 - if ((pCounter->CounterType & PERF_MULTI_COUNTER) == PERF_MULTI_COUNTER) {
120 - ++pullData;
121 - pRawData->MultiCounterData = *(DWORD*)pullData;
122 - }
123 - break;
124 -
125 - //These counters do not use any time reference.
126 - case PERF_COUNTER_RAWCOUNT:
127 - case PERF_COUNTER_RAWCOUNT_HEX:
128 - case PERF_COUNTER_DELTA:
129 - // some counters in these categories, have CounterSize = sizeof(ULONGLONG)
130 - // but the official documentation always uses them as sizeof(DWORD)
131 - pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
132 - pRawData->Time = 0;
133 - break;
134 -
135 - case PERF_COUNTER_LARGE_RAWCOUNT:
136 - case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
137 - case PERF_COUNTER_LARGE_DELTA:
138 - pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
139 - pRawData->Time = 0;
140 - break;
141 -
142 - //These counters use the 100ns time base in their calculation.
143 - case PERF_100NSEC_TIMER:
144 - case PERF_100NSEC_TIMER_INV:
145 - case PERF_100NSEC_MULTI_TIMER:
146 - case PERF_100NSEC_MULTI_TIMER_INV:
147 - pullData = (UNALIGNED ULONGLONG*)pData;
148 - pRawData->Data = *pullData;
149 - pRawData->Time = pDataBlock->PerfTime100nSec.QuadPart;
150 -
151 - //These counter types have a second counter value that is adjacent to
152 - //this counter value in the counter data block. The value is needed for
153 - //the calculation.
154 - if ((pCounter->CounterType & PERF_MULTI_COUNTER) == PERF_MULTI_COUNTER) {
155 - ++pullData;
156 - pRawData->MultiCounterData = *(DWORD*)pullData;
157 - }
158 - break;
159 -
160 - //These counters use two data points, this value and one from this counter's
161 - //base counter. The base counter should be the next counter in the object's
162 - //list of counters.
163 - case PERF_SAMPLE_FRACTION:
164 - case PERF_RAW_FRACTION:
165 - pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
166 - pBaseCounter = pCounter + 1; //Get base counter
167 - if ((pBaseCounter->CounterType & PERF_COUNTER_BASE) == PERF_COUNTER_BASE) {
168 - pData = (PVOID)((LPBYTE)pCounterDataBlock + pBaseCounter->CounterOffset);
169 - pRawData->Time = (LONGLONG)(*(DWORD*)pData);
170 - }
171 - else
172 - fSuccess = FALSE;
173 - break;
174 -
175 - case PERF_LARGE_RAW_FRACTION:
176 - case PERF_PRECISION_SYSTEM_TIMER:
177 - case PERF_PRECISION_100NS_TIMER:
178 - case PERF_PRECISION_OBJECT_TIMER:
179 - pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
180 - pBaseCounter = pCounter + 1;
181 - if ((pBaseCounter->CounterType & PERF_COUNTER_BASE) == PERF_COUNTER_BASE) {
182 - pData = (PVOID)((LPBYTE)pCounterDataBlock + pBaseCounter->CounterOffset);
183 - pRawData->Time = *(LONGLONG*)pData;
184 - }
185 - else
186 - fSuccess = FALSE;
187 - break;
188 -
189 - case PERF_AVERAGE_TIMER:
190 - case PERF_AVERAGE_BULK:
191 - pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
192 - pBaseCounter = pCounter+1;
193 - if ((pBaseCounter->CounterType & PERF_COUNTER_BASE) == PERF_COUNTER_BASE) {
194 - pData = (PVOID)((LPBYTE)pCounterDataBlock + pBaseCounter->CounterOffset);
195 - pRawData->Time = *(DWORD*)pData;
196 - }
197 - else
198 - fSuccess = FALSE;
199 -
200 - if (pCounter->CounterType == PERF_AVERAGE_TIMER)
201 - pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
202 - break;
203 -
204 - //These are base counters and are used in calculations for other counters.
205 - //This case should never be entered.
206 - case PERF_SAMPLE_BASE:
207 - case PERF_AVERAGE_BASE:
208 - case PERF_COUNTER_MULTI_BASE:
209 - case PERF_RAW_BASE:
210 - case PERF_LARGE_RAW_BASE:
211 - pRawData->Data = 0;
212 - pRawData->Time = 0;
213 - fSuccess = FALSE;
214 - break;
215 -
216 - case PERF_ELAPSED_TIME:
217 - pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
218 - pRawData->Time = pObject->PerfTime.QuadPart;
219 - pRawData->Frequency = pObject->PerfFreq.QuadPart;
220 - break;
221 -
222 - //These counters are currently not supported.
223 - case PERF_COUNTER_TEXT:
224 - case PERF_COUNTER_NODATA:
225 - case PERF_COUNTER_HISTOGRAM_TYPE:
226 - default: // unknown counter types
227 - pRawData->Data = 0;
228 - pRawData->Time = 0;
229 - fSuccess = FALSE;
230 - break;
231 - }
232 -
233 - return fSuccess;
234 -}
235 -
236 -// --------------------------------------------------------------------------------------------------------------------
237 -
238 -static inline BOOL isValidPointer(PERF_DATA_BLOCK *pDataBlock __maybe_unused, void *ptr __maybe_unused) {
239 -#ifdef NETDATA_INTERNAL_CHECKS
240 - return (PBYTE)ptr >= (PBYTE)pDataBlock + pDataBlock->TotalByteLength ? FALSE : TRUE;
241 -#else
242 - return TRUE;
243 -#endif
244 -}
245 -
246 -static inline BOOL isValidStructure(PERF_DATA_BLOCK *pDataBlock __maybe_unused, void *ptr __maybe_unused, size_t length __maybe_unused) {
247 -#ifdef NETDATA_INTERNAL_CHECKS
248 - return (PBYTE)ptr + length > (PBYTE)pDataBlock + pDataBlock->TotalByteLength ? FALSE : TRUE;
249 -#else
250 - return TRUE;
251 -#endif
252 -}
253 -
254 -static inline PERF_DATA_BLOCK *getDataBlock(BYTE *pBuffer) {
255 - PERF_DATA_BLOCK *pDataBlock = (PERF_DATA_BLOCK *)pBuffer;
256 -
257 - static WCHAR signature[] = { 'P', 'E', 'R', 'F' };
258 -
259 - if(memcmp(pDataBlock->Signature, signature, sizeof(signature)) != 0) {
260 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
261 - "WINDOWS: PERFLIB: Invalid data block signature.");
262 - return NULL;
263 - }
264 -
265 - if(!isValidPointer(pDataBlock, (PBYTE)pDataBlock + pDataBlock->SystemNameOffset) ||
266 - !isValidStructure(pDataBlock, (PBYTE)pDataBlock + pDataBlock->SystemNameOffset, pDataBlock->SystemNameLength)) {
267 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
268 - "WINDOWS: PERFLIB: Invalid system name array.");
269 - return NULL;
270 - }
271 -
272 - return pDataBlock;
273 -}
274 -
275 -static inline PERF_OBJECT_TYPE *getObjectType(PERF_DATA_BLOCK* pDataBlock, PERF_OBJECT_TYPE *lastObjectType) {
276 - PERF_OBJECT_TYPE* pObjectType = NULL;
277 -
278 - if(!lastObjectType)
279 - pObjectType = (PERF_OBJECT_TYPE *)((PBYTE)pDataBlock + pDataBlock->HeaderLength);
280 - else if (lastObjectType->TotalByteLength != 0)
281 - pObjectType = (PERF_OBJECT_TYPE *)((PBYTE)lastObjectType + lastObjectType->TotalByteLength);
282 -
283 - if(pObjectType && (!isValidPointer(pDataBlock, pObjectType) || !isValidStructure(pDataBlock, pObjectType, pObjectType->TotalByteLength))) {
284 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
285 - "WINDOWS: PERFLIB: Invalid ObjectType!");
286 - pObjectType = NULL;
287 - }
288 -
289 - return pObjectType;
290 -}
291 -
292 -inline PERF_OBJECT_TYPE *getObjectTypeByIndex(PERF_DATA_BLOCK *pDataBlock, DWORD ObjectNameTitleIndex) {
293 - PERF_OBJECT_TYPE *po = NULL;
294 - for(DWORD o = 0; o < pDataBlock->NumObjectTypes ; o++) {
295 - po = getObjectType(pDataBlock, po);
296 - if(po->ObjectNameTitleIndex == ObjectNameTitleIndex)
297 - return po;
298 - }
299 -
300 - return NULL;
301 -}
302 -
303 -static inline PERF_INSTANCE_DEFINITION *getInstance(
304 - PERF_DATA_BLOCK *pDataBlock,
305 - PERF_OBJECT_TYPE *pObjectType,
306 - PERF_COUNTER_BLOCK *lastCounterBlock
307 -) {
308 - PERF_INSTANCE_DEFINITION *pInstance;
309 -
310 - if(!lastCounterBlock)
311 - pInstance = (PERF_INSTANCE_DEFINITION *)((PBYTE)pObjectType + pObjectType->DefinitionLength);
312 - else
313 - pInstance = (PERF_INSTANCE_DEFINITION *)((PBYTE)lastCounterBlock + lastCounterBlock->ByteLength);
314 -
315 - if(pInstance && (!isValidPointer(pDataBlock, pInstance) || !isValidStructure(pDataBlock, pInstance, pInstance->ByteLength))) {
316 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
317 - "WINDOWS: PERFLIB: Invalid Instance Definition!");
318 - pInstance = NULL;
319 - }
320 -
321 - return pInstance;
322 -}
323 -
324 -static inline PERF_COUNTER_BLOCK *getObjectTypeCounterBlock(
325 - PERF_DATA_BLOCK *pDataBlock,
326 - PERF_OBJECT_TYPE *pObjectType
327 -) {
328 - PERF_COUNTER_BLOCK *pCounterBlock = (PERF_COUNTER_BLOCK *)((PBYTE)pObjectType + pObjectType->DefinitionLength);
329 -
330 - if(pCounterBlock && (!isValidPointer(pDataBlock, pCounterBlock) || !isValidStructure(pDataBlock, pCounterBlock, pCounterBlock->ByteLength))) {
331 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
332 - "WINDOWS: PERFLIB: Invalid ObjectType CounterBlock!");
333 - pCounterBlock = NULL;
334 - }
335 -
336 - return pCounterBlock;
337 -}
338 -
339 -static inline PERF_COUNTER_BLOCK *getInstanceCounterBlock(
340 - PERF_DATA_BLOCK *pDataBlock,
341 - PERF_OBJECT_TYPE *pObjectType,
342 - PERF_INSTANCE_DEFINITION *pInstance
343 -) {
344 - (void)pObjectType;
345 - PERF_COUNTER_BLOCK *pCounterBlock = (PERF_COUNTER_BLOCK *)((PBYTE)pInstance + pInstance->ByteLength);
346 -
347 - if(pCounterBlock && (!isValidPointer(pDataBlock, pCounterBlock) || !isValidStructure(pDataBlock, pCounterBlock, pCounterBlock->ByteLength))) {
348 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
349 - "WINDOWS: PERFLIB: Invalid Instance CounterBlock!");
350 - pCounterBlock = NULL;
351 - }
352 -
353 - return pCounterBlock;
354 -}
355 -
356 -inline PERF_INSTANCE_DEFINITION *getInstanceByPosition(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, DWORD instancePosition) {
357 - PERF_INSTANCE_DEFINITION *pi = NULL;
358 - PERF_COUNTER_BLOCK *pc = NULL;
359 - for(DWORD i = 0; i <= instancePosition ;i++) {
360 - pi = getInstance(pDataBlock, pObjectType, pc);
361 - pc = getInstanceCounterBlock(pDataBlock, pObjectType, pi);
362 - }
363 - return pi;
364 -}
365 -
366 -static inline PERF_COUNTER_DEFINITION *getCounterDefinition(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_COUNTER_DEFINITION *lastCounterDefinition) {
367 - PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
368 -
369 - if(!lastCounterDefinition)
370 - pCounterDefinition = (PERF_COUNTER_DEFINITION *)((PBYTE)pObjectType + pObjectType->HeaderLength);
371 - else
372 - pCounterDefinition = (PERF_COUNTER_DEFINITION *)((PBYTE)lastCounterDefinition + lastCounterDefinition->ByteLength);
373 -
374 - if(pCounterDefinition && (!isValidPointer(pDataBlock, pCounterDefinition) || !isValidStructure(pDataBlock, pCounterDefinition, pCounterDefinition->ByteLength))) {
375 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
376 - "WINDOWS: PERFLIB: Invalid Counter Definition!");
377 - pCounterDefinition = NULL;
378 - }
379 -
380 - return pCounterDefinition;
381 -}
382 -
383 -// --------------------------------------------------------------------------------------------------------------------
384 -
385 -static inline BOOL getEncodedStringToUTF8(char *dst, size_t dst_len, DWORD CodePage, char *start, DWORD length) {
386 - WCHAR *tempBuffer; // Temporary buffer for Unicode data
387 - DWORD charsCopied = 0;
388 - BOOL free_tempBuffer;
389 -
390 - if (CodePage == 0) {
391 - // Input is already Unicode (UTF-16)
392 - tempBuffer = (WCHAR *)start;
393 - charsCopied = length / sizeof(WCHAR); // Convert byte length to number of WCHARs
394 - free_tempBuffer = FALSE;
395 - }
396 - else {
397 - // Convert the multi-byte instance name to Unicode (UTF-16)
398 - // Calculate maximum possible characters in UTF-16
399 -
400 - int charCount = MultiByteToWideChar(CodePage, 0, start, (int)length, NULL, 0);
401 - tempBuffer = (WCHAR *)malloc(charCount * sizeof(WCHAR));
402 - if (!tempBuffer) return FALSE;
403 -
404 - charsCopied = MultiByteToWideChar(CodePage, 0, start, (int)length, tempBuffer, charCount);
405 - if (charsCopied == 0) {
406 - free(tempBuffer);
407 - dst[0] = '\0';
408 - return FALSE;
409 - }
410 -
411 - free_tempBuffer = TRUE;
412 - }
413 -
414 - // Now convert from Unicode (UTF-16) to UTF-8
415 - int bytesCopied = WideCharToMultiByte(CP_UTF8, 0, tempBuffer, (int)charsCopied, dst, (int)dst_len, NULL, NULL);
416 - if (bytesCopied == 0) {
417 - if (free_tempBuffer) free(tempBuffer);
418 - dst[0] = '\0'; // Ensure the buffer is null-terminated even on failure
419 - return FALSE;
420 - }
421 -
422 - dst[bytesCopied] = '\0'; // Ensure buffer is null-terminated
423 - if (free_tempBuffer) free(tempBuffer); // Free temporary buffer if used
424 - return TRUE;
425 -}
426 -
427 -inline BOOL getInstanceName(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance,
428 - char *buffer, size_t bufferLen) {
429 - (void)pDataBlock;
430 - if (!pInstance || !buffer || !bufferLen) return FALSE;
431 -
432 - return getEncodedStringToUTF8(buffer, bufferLen, pObjectType->CodePage,
433 - ((char *)pInstance + pInstance->NameOffset), pInstance->NameLength);
434 -}
435 -
436 -inline BOOL getSystemName(PERF_DATA_BLOCK *pDataBlock, char *buffer, size_t bufferLen) {
437 - return getEncodedStringToUTF8(buffer, bufferLen, 0,
438 - ((char *)pDataBlock + pDataBlock->SystemNameOffset), pDataBlock->SystemNameLength);
439 -}
440 -
441 -inline bool ObjectTypeHasInstances(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType) {
442 - (void)pDataBlock;
443 - return pObjectType->NumInstances != PERF_NO_INSTANCES && pObjectType->NumInstances > 0;
444 -}
445 -
446 -PERF_OBJECT_TYPE *perflibFindObjectTypeByName(PERF_DATA_BLOCK *pDataBlock, const char *name) {
447 - PERF_OBJECT_TYPE* pObjectType = NULL;
448 - for(DWORD o = 0; o < pDataBlock->NumObjectTypes; o++) {
449 - pObjectType = getObjectType(pDataBlock, pObjectType);
450 - if(strcmp(name, RegistryFindNameByID(pObjectType->ObjectNameTitleIndex)) == 0)
451 - return pObjectType;
452 - }
453 -
454 - return NULL;
455 -}
456 -
457 -PERF_INSTANCE_DEFINITION *perflibForEachInstance(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *lastInstance) {
458 - if(!ObjectTypeHasInstances(pDataBlock, pObjectType))
459 - return NULL;
460 -
461 - return getInstance(pDataBlock, pObjectType,
462 - lastInstance ?
463 - getInstanceCounterBlock(pDataBlock, pObjectType, lastInstance) :
464 - NULL );
465 -}
466 -
467 -bool perflibGetInstanceCounter(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, COUNTER_DATA *cd) {
468 - PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
469 - for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
470 - pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
471 - if(!pCounterDefinition) {
472 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
473 - "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
474 - c, pObjectType->NumCounters);
475 - break;
476 - }
477 -
478 - if(cd->id) {
479 - if(cd->id != pCounterDefinition->CounterNameTitleIndex)
480 - continue;
481 - }
482 - else {
483 - if(strcmp(RegistryFindNameByID(pCounterDefinition->CounterNameTitleIndex), cd->key) != 0)
484 - continue;
485 -
486 - cd->id = pCounterDefinition->CounterNameTitleIndex;
487 - }
488 -
489 - cd->current.CounterType = cd->OverwriteCounterType ? cd->OverwriteCounterType : pCounterDefinition->CounterType;
490 - PERF_COUNTER_BLOCK *pCounterBlock = getInstanceCounterBlock(pDataBlock, pObjectType, pInstance);
491 -
492 - cd->previous = cd->current;
493 - cd->updated = getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &cd->current);
494 - return cd->updated;
495 - }
496 -
497 - cd->previous = cd->current;
498 - cd->current = RAW_DATA_EMPTY;
499 - cd->updated = false;
500 - return false;
501 -}
502 -
503 -bool perflibGetObjectCounter(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, COUNTER_DATA *cd) {
504 - PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
505 - for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
506 - pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
507 - if(!pCounterDefinition) {
508 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
509 - "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
510 - c, pObjectType->NumCounters);
511 - break;
512 - }
513 -
514 - if(cd->id) {
515 - if(cd->id != pCounterDefinition->CounterNameTitleIndex)
516 - continue;
517 - }
518 - else {
519 - if(strcmp(RegistryFindNameByID(pCounterDefinition->CounterNameTitleIndex), cd->key) != 0)
520 - continue;
521 -
522 - cd->id = pCounterDefinition->CounterNameTitleIndex;
523 - }
524 -
525 - cd->current.CounterType = cd->OverwriteCounterType ? cd->OverwriteCounterType : pCounterDefinition->CounterType;
526 - PERF_COUNTER_BLOCK *pCounterBlock = getObjectTypeCounterBlock(pDataBlock, pObjectType);
527 -
528 - cd->previous = cd->current;
529 - cd->updated = getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &cd->current);
530 - return cd->updated;
531 - }
532 -
533 - cd->previous = cd->current;
534 - cd->current = RAW_DATA_EMPTY;
535 - cd->updated = false;
536 - return false;
537 -}
538 -
539 -PERF_DATA_BLOCK *perflibGetPerformanceData(DWORD id) {
540 - char source[24];
541 - snprintfz(source, sizeof(source), "%u", id);
542 -
543 - LPBYTE pData = (LPBYTE)getPerformanceData((id > 0) ? source : NULL);
544 - if (!pData) return NULL;
545 -
546 - PERF_DATA_BLOCK *pDataBlock = getDataBlock(pData);
547 - if(!pDataBlock) return NULL;
548 -
549 - return pDataBlock;
550 -}
551 -
552 -int perflibQueryAndTraverse(DWORD id,
553 - perflib_data_cb dataCb,
554 - perflib_object_cb objectCb,
555 - perflib_instance_cb instanceCb,
556 - perflib_instance_counter_cb instanceCounterCb,
557 - perflib_counter_cb counterCb,
558 - void *data) {
559 - int counters = -1;
560 -
561 - PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
562 - if(!pDataBlock) goto cleanup;
563 -
564 - bool do_data = true;
565 - if(dataCb)
566 - do_data = dataCb(pDataBlock, data);
567 -
568 - PERF_OBJECT_TYPE* pObjectType = NULL;
569 - for(DWORD o = 0; do_data && o < pDataBlock->NumObjectTypes; o++) {
570 - pObjectType = getObjectType(pDataBlock, pObjectType);
571 - if(!pObjectType) {
572 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
573 - "WINDOWS: PERFLIB: Cannot read object type No %d (out of %d)",
574 - o, pDataBlock->NumObjectTypes);
575 - break;
576 - }
577 -
578 - bool do_object = true;
579 - if(objectCb)
580 - do_object = objectCb(pDataBlock, pObjectType, data);
581 -
582 - if(!do_object)
583 - continue;
584 -
585 - if(ObjectTypeHasInstances(pDataBlock, pObjectType)) {
586 - PERF_INSTANCE_DEFINITION *pInstance = NULL;
587 - PERF_COUNTER_BLOCK *pCounterBlock = NULL;
588 - for(LONG i = 0; i < pObjectType->NumInstances ;i++) {
589 - pInstance = getInstance(pDataBlock, pObjectType, pCounterBlock);
590 - if(!pInstance) {
591 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
592 - "WINDOWS: PERFLIB: Cannot read Instance No %d (out of %d)",
593 - i, pObjectType->NumInstances);
594 - break;
595 - }
596 -
597 - pCounterBlock = getInstanceCounterBlock(pDataBlock, pObjectType, pInstance);
598 - if(!pCounterBlock) {
599 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
600 - "WINDOWS: PERFLIB: Cannot read CounterBlock of instance No %d (out of %d)",
601 - i, pObjectType->NumInstances);
602 - break;
603 - }
604 -
605 - bool do_instance = true;
606 - if(instanceCb)
607 - do_instance = instanceCb(pDataBlock, pObjectType, pInstance, data);
608 -
609 - if(!do_instance)
610 - continue;
611 -
612 - PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
613 - for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
614 - pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
615 - if(!pCounterDefinition) {
616 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
617 - "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
618 - c, pObjectType->NumCounters);
619 - break;
620 - }
621 -
622 - RAW_DATA sample = {
623 - .CounterType = pCounterDefinition->CounterType,
624 - };
625 - if(getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &sample)) {
626 - // DisplayCalculatedValue(&sample, &sample);
627 -
628 - if(instanceCounterCb) {
629 - instanceCounterCb(pDataBlock, pObjectType, pInstance, pCounterDefinition, &sample, data);
630 - counters++;
631 - }
632 - }
633 - }
634 -
635 - if(instanceCb)
636 - instanceCb(pDataBlock, pObjectType, NULL, data);
637 - }
638 - }
639 - else {
640 - PERF_COUNTER_BLOCK *pCounterBlock = getObjectTypeCounterBlock(pDataBlock, pObjectType);
641 - PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
642 - for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
643 - pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
644 - if(!pCounterDefinition) {
645 - nd_log(NDLS_COLLECTORS, NDLP_ERR,
646 - "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
647 - c, pObjectType->NumCounters);
648 - break;
649 - }
650 -
651 - RAW_DATA sample = {
652 - .CounterType = pCounterDefinition->CounterType,
653 - };
654 - if(getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &sample)) {
655 - // DisplayCalculatedValue(&sample, &sample);
656 -
657 - if(counterCb) {
658 - counterCb(pDataBlock, pObjectType, pCounterDefinition, &sample, data);
659 - counters++;
660 - }
661 - }
662 - }
663 - }
664 -
665 - if(objectCb)
666 - objectCb(pDataBlock, NULL, data);
667 - }
668 -
669 -cleanup:
670 - return counters;
671 -}
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "perflib.h"
4 +
5 +#if defined(OS_WINDOWS)
6 +#include <windows.h>
7 +
8 +// --------------------------------------------------------------------------------
9 +
10 +// Retrieve a buffer that contains the specified performance data.
11 +// The pwszSource parameter determines the data that GetRegistryBuffer returns.
12 +//
13 +// Typically, when calling RegQueryValueEx, you can specify zero for the size of the buffer
14 +// and the RegQueryValueEx will set your size variable to the required buffer size. However,
15 +// if the source is "Global" or one or more object index values, you will need to increment
16 +// the buffer size in a loop until RegQueryValueEx does not return ERROR_MORE_DATA.
17 +static LPBYTE getPerformanceData(const char *pwszSource) {
18 + static __thread DWORD size = 0;
19 + static __thread LPBYTE buffer = NULL;
20 +
21 + if(pwszSource == (const char *)0x01) {
22 + freez(buffer);
23 + buffer = NULL;
24 + size = 0;
25 + return NULL;
26 + }
27 +
28 + if(!size) {
29 + size = 32 * 1024;
30 + buffer = mallocz(size);
31 + }
32 +
33 + LONG status = ERROR_SUCCESS;
34 + while ((status = RegQueryValueEx(HKEY_PERFORMANCE_DATA, pwszSource,
35 + NULL, NULL, buffer, &size)) == ERROR_MORE_DATA) {
36 + size *= 2;
37 + buffer = reallocz(buffer, size);
38 + }
39 +
40 + if (status != ERROR_SUCCESS) {
41 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "RegQueryValueEx failed with 0x%x.\n", status);
42 + return NULL;
43 + }
44 +
45 + return buffer;
46 +}
47 +
48 +void perflibFreePerformanceData(void) {
49 + getPerformanceData((const char *)0x01);
50 +}
51 +
52 +// --------------------------------------------------------------------------------------------------------------------
53 +
54 +// Retrieve the raw counter value and any supporting data needed to calculate
55 +// a displayable counter value. Use the counter type to determine the information
56 +// needed to calculate the value.
57 +
58 +static BOOL getCounterData(
59 + PERF_DATA_BLOCK *pDataBlock,
60 + PERF_OBJECT_TYPE* pObject,
61 + PERF_COUNTER_DEFINITION* pCounter,
62 + PERF_COUNTER_BLOCK* pCounterDataBlock,
63 + PRAW_DATA pRawData)
64 +{
65 + PVOID pData = NULL;
66 + UNALIGNED ULONGLONG* pullData = NULL;
67 + PERF_COUNTER_DEFINITION* pBaseCounter = NULL;
68 + BOOL fSuccess = TRUE;
69 +
70 + //Point to the raw counter data.
71 + pData = (PVOID)((LPBYTE)pCounterDataBlock + pCounter->CounterOffset);
72 +
73 + //Now use the PERF_COUNTER_DEFINITION.CounterType value to figure out what
74 + //other information you need to calculate a displayable value.
75 + switch (pCounter->CounterType) {
76 +
77 + case PERF_COUNTER_COUNTER:
78 + case PERF_COUNTER_QUEUELEN_TYPE:
79 + case PERF_SAMPLE_COUNTER:
80 + pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
81 + pRawData->Time = pDataBlock->PerfTime.QuadPart;
82 + if (PERF_COUNTER_COUNTER == pCounter->CounterType || PERF_SAMPLE_COUNTER == pCounter->CounterType)
83 + pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
84 + break;
85 +
86 + case PERF_OBJ_TIME_TIMER:
87 + pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
88 + pRawData->Time = pObject->PerfTime.QuadPart;
89 + break;
90 +
91 + case PERF_COUNTER_100NS_QUEUELEN_TYPE:
92 + pRawData->Data = *(UNALIGNED ULONGLONG *)pData;
93 + pRawData->Time = pDataBlock->PerfTime100nSec.QuadPart;
94 + break;
95 +
96 + case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE:
97 + pRawData->Data = *(UNALIGNED ULONGLONG *)pData;
98 + pRawData->Time = pObject->PerfTime.QuadPart;
99 + break;
100 +
101 + case PERF_COUNTER_TIMER:
102 + case PERF_COUNTER_TIMER_INV:
103 + case PERF_COUNTER_BULK_COUNT:
104 + case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
105 + pullData = (UNALIGNED ULONGLONG *)pData;
106 + pRawData->Data = *pullData;
107 + pRawData->Time = pDataBlock->PerfTime.QuadPart;
108 + if (pCounter->CounterType == PERF_COUNTER_BULK_COUNT)
109 + pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
110 + break;
111 +
112 + case PERF_COUNTER_MULTI_TIMER:
113 + case PERF_COUNTER_MULTI_TIMER_INV:
114 + pullData = (UNALIGNED ULONGLONG *)pData;
115 + pRawData->Data = *pullData;
116 + pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
117 + pRawData->Time = pDataBlock->PerfTime.QuadPart;
118 +
119 + //These counter types have a second counter value that is adjacent to
120 + //this counter value in the counter data block. The value is needed for
121 + //the calculation.
122 + if ((pCounter->CounterType & PERF_MULTI_COUNTER) == PERF_MULTI_COUNTER) {
123 + ++pullData;
124 + pRawData->MultiCounterData = *(DWORD*)pullData;
125 + }
126 + break;
127 +
128 + //These counters do not use any time reference.
129 + case PERF_COUNTER_RAWCOUNT:
130 + case PERF_COUNTER_RAWCOUNT_HEX:
131 + case PERF_COUNTER_DELTA:
132 + // some counters in these categories, have CounterSize = sizeof(ULONGLONG)
133 + // but the official documentation always uses them as sizeof(DWORD)
134 + pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
135 + pRawData->Time = 0;
136 + break;
137 +
138 + case PERF_COUNTER_LARGE_RAWCOUNT:
139 + case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
140 + case PERF_COUNTER_LARGE_DELTA:
141 + pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
142 + pRawData->Time = 0;
143 + break;
144 +
145 + //These counters use the 100ns time base in their calculation.
146 + case PERF_100NSEC_TIMER:
147 + case PERF_100NSEC_TIMER_INV:
148 + case PERF_100NSEC_MULTI_TIMER:
149 + case PERF_100NSEC_MULTI_TIMER_INV:
150 + pullData = (UNALIGNED ULONGLONG*)pData;
151 + pRawData->Data = *pullData;
152 + pRawData->Time = pDataBlock->PerfTime100nSec.QuadPart;
153 +
154 + //These counter types have a second counter value that is adjacent to
155 + //this counter value in the counter data block. The value is needed for
156 + //the calculation.
157 + if ((pCounter->CounterType & PERF_MULTI_COUNTER) == PERF_MULTI_COUNTER) {
158 + ++pullData;
159 + pRawData->MultiCounterData = *(DWORD*)pullData;
160 + }
161 + break;
162 +
163 + //These counters use two data points, this value and one from this counter's
164 + //base counter. The base counter should be the next counter in the object's
165 + //list of counters.
166 + case PERF_SAMPLE_FRACTION:
167 + case PERF_RAW_FRACTION:
168 + pRawData->Data = (ULONGLONG)(*(DWORD*)pData);
169 + pBaseCounter = pCounter + 1; //Get base counter
170 + if ((pBaseCounter->CounterType & PERF_COUNTER_BASE) == PERF_COUNTER_BASE) {
171 + pData = (PVOID)((LPBYTE)pCounterDataBlock + pBaseCounter->CounterOffset);
172 + pRawData->Time = (LONGLONG)(*(DWORD*)pData);
173 + }
174 + else
175 + fSuccess = FALSE;
176 + break;
177 +
178 + case PERF_LARGE_RAW_FRACTION:
179 + case PERF_PRECISION_SYSTEM_TIMER:
180 + case PERF_PRECISION_100NS_TIMER:
181 + case PERF_PRECISION_OBJECT_TIMER:
182 + pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
183 + pBaseCounter = pCounter + 1;
184 + if ((pBaseCounter->CounterType & PERF_COUNTER_BASE) == PERF_COUNTER_BASE) {
185 + pData = (PVOID)((LPBYTE)pCounterDataBlock + pBaseCounter->CounterOffset);
186 + pRawData->Time = *(LONGLONG*)pData;
187 + }
188 + else
189 + fSuccess = FALSE;
190 + break;
191 +
192 + case PERF_AVERAGE_TIMER:
193 + case PERF_AVERAGE_BULK:
194 + pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
195 + pBaseCounter = pCounter+1;
196 + if ((pBaseCounter->CounterType & PERF_COUNTER_BASE) == PERF_COUNTER_BASE) {
197 + pData = (PVOID)((LPBYTE)pCounterDataBlock + pBaseCounter->CounterOffset);
198 + pRawData->Time = *(DWORD*)pData;
199 + }
200 + else
201 + fSuccess = FALSE;
202 +
203 + if (pCounter->CounterType == PERF_AVERAGE_TIMER)
204 + pRawData->Frequency = pDataBlock->PerfFreq.QuadPart;
205 + break;
206 +
207 + //These are base counters and are used in calculations for other counters.
208 + //This case should never be entered.
209 + case PERF_SAMPLE_BASE:
210 + case PERF_AVERAGE_BASE:
211 + case PERF_COUNTER_MULTI_BASE:
212 + case PERF_RAW_BASE:
213 + case PERF_LARGE_RAW_BASE:
214 + pRawData->Data = 0;
215 + pRawData->Time = 0;
216 + fSuccess = FALSE;
217 + break;
218 +
219 + case PERF_ELAPSED_TIME:
220 + pRawData->Data = *(UNALIGNED ULONGLONG*)pData;
221 + pRawData->Time = pObject->PerfTime.QuadPart;
222 + pRawData->Frequency = pObject->PerfFreq.QuadPart;
223 + break;
224 +
225 + //These counters are currently not supported.
226 + case PERF_COUNTER_TEXT:
227 + case PERF_COUNTER_NODATA:
228 + case PERF_COUNTER_HISTOGRAM_TYPE:
229 + default: // unknown counter types
230 + pRawData->Data = 0;
231 + pRawData->Time = 0;
232 + fSuccess = FALSE;
233 + break;
234 + }
235 +
236 + return fSuccess;
237 +}
238 +
239 +// --------------------------------------------------------------------------------------------------------------------
240 +
241 +static inline BOOL isValidPointer(PERF_DATA_BLOCK *pDataBlock __maybe_unused, void *ptr __maybe_unused) {
242 +#ifdef NETDATA_INTERNAL_CHECKS
243 + return (PBYTE)ptr >= (PBYTE)pDataBlock + pDataBlock->TotalByteLength ? FALSE : TRUE;
244 +#else
245 + return TRUE;
246 +#endif
247 +}
248 +
249 +static inline BOOL isValidStructure(PERF_DATA_BLOCK *pDataBlock __maybe_unused, void *ptr __maybe_unused, size_t length __maybe_unused) {
250 +#ifdef NETDATA_INTERNAL_CHECKS
251 + return (PBYTE)ptr + length > (PBYTE)pDataBlock + pDataBlock->TotalByteLength ? FALSE : TRUE;
252 +#else
253 + return TRUE;
254 +#endif
255 +}
256 +
257 +static inline PERF_DATA_BLOCK *getDataBlock(BYTE *pBuffer) {
258 + PERF_DATA_BLOCK *pDataBlock = (PERF_DATA_BLOCK *)pBuffer;
259 +
260 + static WCHAR signature[] = { 'P', 'E', 'R', 'F' };
261 +
262 + if(memcmp(pDataBlock->Signature, signature, sizeof(signature)) != 0) {
263 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
264 + "WINDOWS: PERFLIB: Invalid data block signature.");
265 + return NULL;
266 + }
267 +
268 + if(!isValidPointer(pDataBlock, (PBYTE)pDataBlock + pDataBlock->SystemNameOffset) ||
269 + !isValidStructure(pDataBlock, (PBYTE)pDataBlock + pDataBlock->SystemNameOffset, pDataBlock->SystemNameLength)) {
270 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
271 + "WINDOWS: PERFLIB: Invalid system name array.");
272 + return NULL;
273 + }
274 +
275 + return pDataBlock;
276 +}
277 +
278 +static inline PERF_OBJECT_TYPE *getObjectType(PERF_DATA_BLOCK* pDataBlock, PERF_OBJECT_TYPE *lastObjectType) {
279 + PERF_OBJECT_TYPE* pObjectType = NULL;
280 +
281 + if(!lastObjectType)
282 + pObjectType = (PERF_OBJECT_TYPE *)((PBYTE)pDataBlock + pDataBlock->HeaderLength);
283 + else if (lastObjectType->TotalByteLength != 0)
284 + pObjectType = (PERF_OBJECT_TYPE *)((PBYTE)lastObjectType + lastObjectType->TotalByteLength);
285 +
286 + if(pObjectType && (!isValidPointer(pDataBlock, pObjectType) || !isValidStructure(pDataBlock, pObjectType, pObjectType->TotalByteLength))) {
287 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
288 + "WINDOWS: PERFLIB: Invalid ObjectType!");
289 + pObjectType = NULL;
290 + }
291 +
292 + return pObjectType;
293 +}
294 +
295 +inline PERF_OBJECT_TYPE *getObjectTypeByIndex(PERF_DATA_BLOCK *pDataBlock, DWORD ObjectNameTitleIndex) {
296 + PERF_OBJECT_TYPE *po = NULL;
297 + for(DWORD o = 0; o < pDataBlock->NumObjectTypes ; o++) {
298 + po = getObjectType(pDataBlock, po);
299 + if(po->ObjectNameTitleIndex == ObjectNameTitleIndex)
300 + return po;
301 + }
302 +
303 + return NULL;
304 +}
305 +
306 +static inline PERF_INSTANCE_DEFINITION *getInstance(
307 + PERF_DATA_BLOCK *pDataBlock,
308 + PERF_OBJECT_TYPE *pObjectType,
309 + PERF_COUNTER_BLOCK *lastCounterBlock
310 +) {
311 + PERF_INSTANCE_DEFINITION *pInstance;
312 +
313 + if(!lastCounterBlock)
314 + pInstance = (PERF_INSTANCE_DEFINITION *)((PBYTE)pObjectType + pObjectType->DefinitionLength);
315 + else
316 + pInstance = (PERF_INSTANCE_DEFINITION *)((PBYTE)lastCounterBlock + lastCounterBlock->ByteLength);
317 +
318 + if(pInstance && (!isValidPointer(pDataBlock, pInstance) || !isValidStructure(pDataBlock, pInstance, pInstance->ByteLength))) {
319 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
320 + "WINDOWS: PERFLIB: Invalid Instance Definition!");
321 + pInstance = NULL;
322 + }
323 +
324 + return pInstance;
325 +}
326 +
327 +static inline PERF_COUNTER_BLOCK *getObjectTypeCounterBlock(
328 + PERF_DATA_BLOCK *pDataBlock,
329 + PERF_OBJECT_TYPE *pObjectType
330 +) {
331 + PERF_COUNTER_BLOCK *pCounterBlock = (PERF_COUNTER_BLOCK *)((PBYTE)pObjectType + pObjectType->DefinitionLength);
332 +
333 + if(pCounterBlock && (!isValidPointer(pDataBlock, pCounterBlock) || !isValidStructure(pDataBlock, pCounterBlock, pCounterBlock->ByteLength))) {
334 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
335 + "WINDOWS: PERFLIB: Invalid ObjectType CounterBlock!");
336 + pCounterBlock = NULL;
337 + }
338 +
339 + return pCounterBlock;
340 +}
341 +
342 +static inline PERF_COUNTER_BLOCK *getInstanceCounterBlock(
343 + PERF_DATA_BLOCK *pDataBlock,
344 + PERF_OBJECT_TYPE *pObjectType,
345 + PERF_INSTANCE_DEFINITION *pInstance
346 +) {
347 + (void)pObjectType;
348 + PERF_COUNTER_BLOCK *pCounterBlock = (PERF_COUNTER_BLOCK *)((PBYTE)pInstance + pInstance->ByteLength);
349 +
350 + if(pCounterBlock && (!isValidPointer(pDataBlock, pCounterBlock) || !isValidStructure(pDataBlock, pCounterBlock, pCounterBlock->ByteLength))) {
351 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
352 + "WINDOWS: PERFLIB: Invalid Instance CounterBlock!");
353 + pCounterBlock = NULL;
354 + }
355 +
356 + return pCounterBlock;
357 +}
358 +
359 +inline PERF_INSTANCE_DEFINITION *getInstanceByPosition(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, DWORD instancePosition) {
360 + PERF_INSTANCE_DEFINITION *pi = NULL;
361 + PERF_COUNTER_BLOCK *pc = NULL;
362 + for(DWORD i = 0; i <= instancePosition ;i++) {
363 + pi = getInstance(pDataBlock, pObjectType, pc);
364 + pc = getInstanceCounterBlock(pDataBlock, pObjectType, pi);
365 + }
366 + return pi;
367 +}
368 +
369 +static inline PERF_COUNTER_DEFINITION *getCounterDefinition(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_COUNTER_DEFINITION *lastCounterDefinition) {
370 + PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
371 +
372 + if(!lastCounterDefinition)
373 + pCounterDefinition = (PERF_COUNTER_DEFINITION *)((PBYTE)pObjectType + pObjectType->HeaderLength);
374 + else
375 + pCounterDefinition = (PERF_COUNTER_DEFINITION *)((PBYTE)lastCounterDefinition + lastCounterDefinition->ByteLength);
376 +
377 + if(pCounterDefinition && (!isValidPointer(pDataBlock, pCounterDefinition) || !isValidStructure(pDataBlock, pCounterDefinition, pCounterDefinition->ByteLength))) {
378 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
379 + "WINDOWS: PERFLIB: Invalid Counter Definition!");
380 + pCounterDefinition = NULL;
381 + }
382 +
383 + return pCounterDefinition;
384 +}
385 +
386 +// --------------------------------------------------------------------------------------------------------------------
387 +
388 +static inline BOOL getEncodedStringToUTF8(char *dst, size_t dst_len, DWORD CodePage, char *start, DWORD length) {
389 + WCHAR *tempBuffer; // Temporary buffer for Unicode data
390 + DWORD charsCopied = 0;
391 + BOOL free_tempBuffer;
392 +
393 + if (CodePage == 0) {
394 + // Input is already Unicode (UTF-16)
395 + tempBuffer = (WCHAR *)start;
396 + charsCopied = length / sizeof(WCHAR); // Convert byte length to number of WCHARs
397 + free_tempBuffer = FALSE;
398 + }
399 + else {
400 + // Convert the multi-byte instance name to Unicode (UTF-16)
401 + // Calculate maximum possible characters in UTF-16
402 +
403 + int charCount = MultiByteToWideChar(CodePage, 0, start, (int)length, NULL, 0);
404 + tempBuffer = (WCHAR *)malloc(charCount * sizeof(WCHAR));
405 + if (!tempBuffer) return FALSE;
406 +
407 + charsCopied = MultiByteToWideChar(CodePage, 0, start, (int)length, tempBuffer, charCount);
408 + if (charsCopied == 0) {
409 + free(tempBuffer);
410 + dst[0] = '\0';
411 + return FALSE;
412 + }
413 +
414 + free_tempBuffer = TRUE;
415 + }
416 +
417 + // Now convert from Unicode (UTF-16) to UTF-8
418 + int bytesCopied = WideCharToMultiByte(CP_UTF8, 0, tempBuffer, (int)charsCopied, dst, (int)dst_len, NULL, NULL);
419 + if (bytesCopied == 0) {
420 + if (free_tempBuffer) free(tempBuffer);
421 + dst[0] = '\0'; // Ensure the buffer is null-terminated even on failure
422 + return FALSE;
423 + }
424 +
425 + dst[bytesCopied] = '\0'; // Ensure buffer is null-terminated
426 + if (free_tempBuffer) free(tempBuffer); // Free temporary buffer if used
427 + return TRUE;
428 +}
429 +
430 +inline BOOL getInstanceName(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance,
431 + char *buffer, size_t bufferLen) {
432 + (void)pDataBlock;
433 + if (!pInstance || !buffer || !bufferLen) return FALSE;
434 +
435 + return getEncodedStringToUTF8(buffer, bufferLen, pObjectType->CodePage,
436 + ((char *)pInstance + pInstance->NameOffset), pInstance->NameLength);
437 +}
438 +
439 +inline BOOL getSystemName(PERF_DATA_BLOCK *pDataBlock, char *buffer, size_t bufferLen) {
440 + return getEncodedStringToUTF8(buffer, bufferLen, 0,
441 + ((char *)pDataBlock + pDataBlock->SystemNameOffset), pDataBlock->SystemNameLength);
442 +}
443 +
444 +inline bool ObjectTypeHasInstances(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType) {
445 + (void)pDataBlock;
446 + return pObjectType->NumInstances != PERF_NO_INSTANCES && pObjectType->NumInstances > 0;
447 +}
448 +
449 +PERF_OBJECT_TYPE *perflibFindObjectTypeByName(PERF_DATA_BLOCK *pDataBlock, const char *name) {
450 + PERF_OBJECT_TYPE* pObjectType = NULL;
451 + for(DWORD o = 0; o < pDataBlock->NumObjectTypes; o++) {
452 + pObjectType = getObjectType(pDataBlock, pObjectType);
453 + if(strcmp(name, RegistryFindNameByID(pObjectType->ObjectNameTitleIndex)) == 0)
454 + return pObjectType;
455 + }
456 +
457 + return NULL;
458 +}
459 +
460 +PERF_INSTANCE_DEFINITION *perflibForEachInstance(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *lastInstance) {
461 + if(!ObjectTypeHasInstances(pDataBlock, pObjectType))
462 + return NULL;
463 +
464 + return getInstance(pDataBlock, pObjectType,
465 + lastInstance ?
466 + getInstanceCounterBlock(pDataBlock, pObjectType, lastInstance) :
467 + NULL );
468 +}
469 +
470 +bool perflibGetInstanceCounter(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, COUNTER_DATA *cd) {
471 + PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
472 + for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
473 + pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
474 + if(!pCounterDefinition) {
475 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
476 + "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
477 + c, pObjectType->NumCounters);
478 + break;
479 + }
480 +
481 + if(cd->id) {
482 + if(cd->id != pCounterDefinition->CounterNameTitleIndex)
483 + continue;
484 + }
485 + else {
486 + if(strcmp(RegistryFindNameByID(pCounterDefinition->CounterNameTitleIndex), cd->key) != 0)
487 + continue;
488 +
489 + cd->id = pCounterDefinition->CounterNameTitleIndex;
490 + }
491 +
492 + cd->current.CounterType = cd->OverwriteCounterType ? cd->OverwriteCounterType : pCounterDefinition->CounterType;
493 + PERF_COUNTER_BLOCK *pCounterBlock = getInstanceCounterBlock(pDataBlock, pObjectType, pInstance);
494 +
495 + cd->previous = cd->current;
496 + cd->updated = getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &cd->current);
497 + return cd->updated;
498 + }
499 +
500 + cd->previous = cd->current;
501 + cd->current = RAW_DATA_EMPTY;
502 + cd->updated = false;
503 + return false;
504 +}
505 +
506 +bool perflibGetObjectCounter(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, COUNTER_DATA *cd) {
507 + PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
508 + for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
509 + pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
510 + if(!pCounterDefinition) {
511 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
512 + "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
513 + c, pObjectType->NumCounters);
514 + break;
515 + }
516 +
517 + if(cd->id) {
518 + if(cd->id != pCounterDefinition->CounterNameTitleIndex)
519 + continue;
520 + }
521 + else {
522 + if(strcmp(RegistryFindNameByID(pCounterDefinition->CounterNameTitleIndex), cd->key) != 0)
523 + continue;
524 +
525 + cd->id = pCounterDefinition->CounterNameTitleIndex;
526 + }
527 +
528 + cd->current.CounterType = cd->OverwriteCounterType ? cd->OverwriteCounterType : pCounterDefinition->CounterType;
529 + PERF_COUNTER_BLOCK *pCounterBlock = getObjectTypeCounterBlock(pDataBlock, pObjectType);
530 +
531 + cd->previous = cd->current;
532 + cd->updated = getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &cd->current);
533 + return cd->updated;
534 + }
535 +
536 + cd->previous = cd->current;
537 + cd->current = RAW_DATA_EMPTY;
538 + cd->updated = false;
539 + return false;
540 +}
541 +
542 +PERF_DATA_BLOCK *perflibGetPerformanceData(DWORD id) {
543 + char source[24];
544 + snprintfz(source, sizeof(source), "%u", id);
545 +
546 + LPBYTE pData = (LPBYTE)getPerformanceData((id > 0) ? source : NULL);
547 + if (!pData) return NULL;
548 +
549 + PERF_DATA_BLOCK *pDataBlock = getDataBlock(pData);
550 + if(!pDataBlock) return NULL;
551 +
552 + return pDataBlock;
553 +}
554 +
555 +int perflibQueryAndTraverse(DWORD id,
556 + perflib_data_cb dataCb,
557 + perflib_object_cb objectCb,
558 + perflib_instance_cb instanceCb,
559 + perflib_instance_counter_cb instanceCounterCb,
560 + perflib_counter_cb counterCb,
561 + void *data) {
562 + int counters = -1;
563 +
564 + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id);
565 + if(!pDataBlock) goto cleanup;
566 +
567 + bool do_data = true;
568 + if(dataCb)
569 + do_data = dataCb(pDataBlock, data);
570 +
571 + PERF_OBJECT_TYPE* pObjectType = NULL;
572 + for(DWORD o = 0; do_data && o < pDataBlock->NumObjectTypes; o++) {
573 + pObjectType = getObjectType(pDataBlock, pObjectType);
574 + if(!pObjectType) {
575 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
576 + "WINDOWS: PERFLIB: Cannot read object type No %d (out of %d)",
577 + o, pDataBlock->NumObjectTypes);
578 + break;
579 + }
580 +
581 + bool do_object = true;
582 + if(objectCb)
583 + do_object = objectCb(pDataBlock, pObjectType, data);
584 +
585 + if(!do_object)
586 + continue;
587 +
588 + if(ObjectTypeHasInstances(pDataBlock, pObjectType)) {
589 + PERF_INSTANCE_DEFINITION *pInstance = NULL;
590 + PERF_COUNTER_BLOCK *pCounterBlock = NULL;
591 + for(LONG i = 0; i < pObjectType->NumInstances ;i++) {
592 + pInstance = getInstance(pDataBlock, pObjectType, pCounterBlock);
593 + if(!pInstance) {
594 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
595 + "WINDOWS: PERFLIB: Cannot read Instance No %d (out of %d)",
596 + i, pObjectType->NumInstances);
597 + break;
598 + }
599 +
600 + pCounterBlock = getInstanceCounterBlock(pDataBlock, pObjectType, pInstance);
601 + if(!pCounterBlock) {
602 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
603 + "WINDOWS: PERFLIB: Cannot read CounterBlock of instance No %d (out of %d)",
604 + i, pObjectType->NumInstances);
605 + break;
606 + }
607 +
608 + bool do_instance = true;
609 + if(instanceCb)
610 + do_instance = instanceCb(pDataBlock, pObjectType, pInstance, data);
611 +
612 + if(!do_instance)
613 + continue;
614 +
615 + PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
616 + for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
617 + pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
618 + if(!pCounterDefinition) {
619 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
620 + "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
621 + c, pObjectType->NumCounters);
622 + break;
623 + }
624 +
625 + RAW_DATA sample = {
626 + .CounterType = pCounterDefinition->CounterType,
627 + };
628 + if(getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &sample)) {
629 + // DisplayCalculatedValue(&sample, &sample);
630 +
631 + if(instanceCounterCb) {
632 + instanceCounterCb(pDataBlock, pObjectType, pInstance, pCounterDefinition, &sample, data);
633 + counters++;
634 + }
635 + }
636 + }
637 +
638 + if(instanceCb)
639 + instanceCb(pDataBlock, pObjectType, NULL, data);
640 + }
641 + }
642 + else {
643 + PERF_COUNTER_BLOCK *pCounterBlock = getObjectTypeCounterBlock(pDataBlock, pObjectType);
644 + PERF_COUNTER_DEFINITION *pCounterDefinition = NULL;
645 + for(DWORD c = 0; c < pObjectType->NumCounters ;c++) {
646 + pCounterDefinition = getCounterDefinition(pDataBlock, pObjectType, pCounterDefinition);
647 + if(!pCounterDefinition) {
648 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
649 + "WINDOWS: PERFLIB: Cannot read counter definition No %u (out of %u)",
650 + c, pObjectType->NumCounters);
651 + break;
652 + }
653 +
654 + RAW_DATA sample = {
655 + .CounterType = pCounterDefinition->CounterType,
656 + };
657 + if(getCounterData(pDataBlock, pObjectType, pCounterDefinition, pCounterBlock, &sample)) {
658 + // DisplayCalculatedValue(&sample, &sample);
659 +
660 + if(counterCb) {
661 + counterCb(pDataBlock, pObjectType, pCounterDefinition, &sample, data);
662 + counters++;
663 + }
664 + }
665 + }
666 + }
667 +
668 + if(objectCb)
669 + objectCb(pDataBlock, NULL, data);
670 + }
671 +
672 +cleanup:
673 + return counters;
674 +}
675 +
676 +#endif // OS_WINDOWS
\ No newline at end of file
src/libnetdata/os/windows-perflib/perflib.h renamed
+88 -72
@@ -1,72 +1,88 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_PERFLIB_H
4 -#define NETDATA_PERFLIB_H
5 -
6 -#include "libnetdata/libnetdata.h"
7 -#include <windows.h>
8 -
9 -const char *RegistryFindNameByID(DWORD id);
10 -const char *RegistryFindHelpByID(DWORD id);
11 -DWORD RegistryFindIDByName(const char *name);
12 -#define PERFLIB_REGISTRY_NAME_NOT_FOUND (DWORD)-1
13 -
14 -PERF_DATA_BLOCK *perflibGetPerformanceData(DWORD id);
15 -void perflibFreePerformanceData(void);
16 -PERF_OBJECT_TYPE *perflibFindObjectTypeByName(PERF_DATA_BLOCK *pDataBlock, const char *name);
17 -PERF_INSTANCE_DEFINITION *perflibForEachInstance(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *lastInstance);
18 -
19 -typedef struct _rawdata {
20 - DWORD CounterType;
21 - DWORD MultiCounterData; // Second raw counter value for multi-valued counters
22 - ULONGLONG Data; // Raw counter data
23 - LONGLONG Time; // Is a time value or a base value
24 - LONGLONG Frequency;
25 -} RAW_DATA, *PRAW_DATA;
26 -
27 -typedef struct _counterdata {
28 - DWORD id;
29 - bool updated;
30 - const char *key;
31 - DWORD OverwriteCounterType; // if set, the counter type will be overwritten once read
32 - RAW_DATA current;
33 - RAW_DATA previous;
34 -} COUNTER_DATA;
35 -
36 -#define RAW_DATA_EMPTY (RAW_DATA){ 0 }
37 -
38 -bool perflibGetInstanceCounter(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, COUNTER_DATA *cd);
39 -bool perflibGetObjectCounter(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, COUNTER_DATA *cd);
40 -
41 -typedef bool (*perflib_data_cb)(PERF_DATA_BLOCK *pDataBlock, void *data);
42 -typedef bool (*perflib_object_cb)(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, void *data);
43 -typedef bool (*perflib_instance_cb)(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, void *data);
44 -typedef bool (*perflib_instance_counter_cb)(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, PERF_COUNTER_DEFINITION *pCounter, RAW_DATA *sample, void *data);
45 -typedef bool (*perflib_counter_cb)(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_COUNTER_DEFINITION *pCounter, RAW_DATA *sample, void *data);
46 -
47 -int perflibQueryAndTraverse(DWORD id,
48 - perflib_data_cb dataCb,
49 - perflib_object_cb objectCb,
50 - perflib_instance_cb instanceCb,
51 - perflib_instance_counter_cb instanceCounterCb,
52 - perflib_counter_cb counterCb,
53 - void *data);
54 -
55 -bool ObjectTypeHasInstances(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType);
56 -
57 -BOOL getInstanceName(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance,
58 - char *buffer, size_t bufferLen);
59 -
60 -BOOL getSystemName(PERF_DATA_BLOCK *pDataBlock, char *buffer, size_t bufferLen);
61 -
62 -PERF_OBJECT_TYPE *getObjectTypeByIndex(PERF_DATA_BLOCK *pDataBlock, DWORD ObjectNameTitleIndex);
63 -
64 -PERF_INSTANCE_DEFINITION *getInstanceByPosition(
65 - PERF_DATA_BLOCK *pDataBlock,
66 - PERF_OBJECT_TYPE *pObjectType,
67 - DWORD instancePosition);
68 -
69 -void PerflibNamesRegistryInitialize(void);
70 -void PerflibNamesRegistryUpdate(void);
71 -
72 -#endif //NETDATA_PERFLIB_H
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PERFLIB_H
4 +#define NETDATA_PERFLIB_H
5 +
6 +#include "libnetdata/libnetdata.h"
7 +
8 +#if defined(OS_WINDOWS)
9 +
10 +typedef uint32_t DWORD;
11 +typedef long long LONGLONG;
12 +typedef unsigned long long ULONGLONG;
13 +typedef int BOOL;
14 +
15 +struct _PERF_DATA_BLOCK;
16 +typedef struct _PERF_DATA_BLOCK PERF_DATA_BLOCK;
17 +struct _PERF_OBJECT_TYPE;
18 +typedef struct _PERF_OBJECT_TYPE PERF_OBJECT_TYPE;
19 +struct _PERF_INSTANCE_DEFINITION;
20 +typedef struct _PERF_INSTANCE_DEFINITION PERF_INSTANCE_DEFINITION;
21 +struct _PERF_COUNTER_DEFINITION;
22 +typedef struct _PERF_COUNTER_DEFINITION PERF_COUNTER_DEFINITION;
23 +
24 +const char *RegistryFindNameByID(DWORD id);
25 +const char *RegistryFindHelpByID(DWORD id);
26 +DWORD RegistryFindIDByName(const char *name);
27 +#define PERFLIB_REGISTRY_NAME_NOT_FOUND (DWORD)-1
28 +
29 +PERF_DATA_BLOCK *perflibGetPerformanceData(DWORD id);
30 +void perflibFreePerformanceData(void);
31 +PERF_OBJECT_TYPE *perflibFindObjectTypeByName(PERF_DATA_BLOCK *pDataBlock, const char *name);
32 +PERF_INSTANCE_DEFINITION *perflibForEachInstance(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *lastInstance);
33 +
34 +typedef struct _rawdata {
35 + DWORD CounterType;
36 + DWORD MultiCounterData; // Second raw counter value for multi-valued counters
37 + ULONGLONG Data; // Raw counter data
38 + LONGLONG Time; // Is a time value or a base value
39 + LONGLONG Frequency;
40 +} RAW_DATA, *PRAW_DATA;
41 +
42 +typedef struct _counterdata {
43 + DWORD id;
44 + bool updated;
45 + const char *key;
46 + DWORD OverwriteCounterType; // if set, the counter type will be overwritten once read
47 + RAW_DATA current;
48 + RAW_DATA previous;
49 +} COUNTER_DATA;
50 +
51 +#define RAW_DATA_EMPTY (RAW_DATA){ 0 }
52 +
53 +bool perflibGetInstanceCounter(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, COUNTER_DATA *cd);
54 +bool perflibGetObjectCounter(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, COUNTER_DATA *cd);
55 +
56 +typedef bool (*perflib_data_cb)(PERF_DATA_BLOCK *pDataBlock, void *data);
57 +typedef bool (*perflib_object_cb)(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, void *data);
58 +typedef bool (*perflib_instance_cb)(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, void *data);
59 +typedef bool (*perflib_instance_counter_cb)(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance, PERF_COUNTER_DEFINITION *pCounter, RAW_DATA *sample, void *data);
60 +typedef bool (*perflib_counter_cb)(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_COUNTER_DEFINITION *pCounter, RAW_DATA *sample, void *data);
61 +
62 +int perflibQueryAndTraverse(DWORD id,
63 + perflib_data_cb dataCb,
64 + perflib_object_cb objectCb,
65 + perflib_instance_cb instanceCb,
66 + perflib_instance_counter_cb instanceCounterCb,
67 + perflib_counter_cb counterCb,
68 + void *data);
69 +
70 +bool ObjectTypeHasInstances(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType);
71 +
72 +BOOL getInstanceName(PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectType, PERF_INSTANCE_DEFINITION *pInstance,
73 + char *buffer, size_t bufferLen);
74 +
75 +BOOL getSystemName(PERF_DATA_BLOCK *pDataBlock, char *buffer, size_t bufferLen);
76 +
77 +PERF_OBJECT_TYPE *getObjectTypeByIndex(PERF_DATA_BLOCK *pDataBlock, DWORD ObjectNameTitleIndex);
78 +
79 +PERF_INSTANCE_DEFINITION *getInstanceByPosition(
80 + PERF_DATA_BLOCK *pDataBlock,
81 + PERF_OBJECT_TYPE *pObjectType,
82 + DWORD instancePosition);
83 +
84 +void PerflibNamesRegistryInitialize(void);
85 +void PerflibNamesRegistryUpdate(void);
86 +
87 +#endif // OS_WINDOWS
88 +#endif //NETDATA_PERFLIB_H
src/libnetdata/procfile/procfile.c
+5 -1
@@ -230,8 +230,12 @@ static void procfile_parser(procfile *ff) {
230 }
231 else if(likely(ct == PF_CHAR_IS_OPEN)) {
232 if(s == t) {
233 + if(!opened)
234 + t = ++s;
235 + else
236 + ++s;
237 +
238 opened++;
234 - t = ++s;
239 }
240 else if(opened) {
241 opened++;
src/libnetdata/sanitizers/chart_id_and_name.c new
+618
@@ -0,0 +1,618 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "../libnetdata.h"
4 +
5 +static uint8_t netdata_map_chart_names[256] = {
6 + [0] = '\0', //
7 + [1] = '_', //
8 + [2] = '_', //
9 + [3] = '_', //
10 + [4] = '_', //
11 + [5] = '_', //
12 + [6] = '_', //
13 + [7] = '_', //
14 + [8] = '_', //
15 + [9] = '_', //
16 + [10] = '_', //
17 + [11] = '_', //
18 + [12] = '_', //
19 + [13] = '_', //
20 + [14] = '_', //
21 + [15] = '_', //
22 + [16] = '_', //
23 + [17] = '_', //
24 + [18] = '_', //
25 + [19] = '_', //
26 + [20] = '_', //
27 + [21] = '_', //
28 + [22] = '_', //
29 + [23] = '_', //
30 + [24] = '_', //
31 + [25] = '_', //
32 + [26] = '_', //
33 + [27] = '_', //
34 + [28] = '_', //
35 + [29] = '_', //
36 + [30] = '_', //
37 + [31] = '_', //
38 + [32] = '_', //
39 + [33] = '_', // !
40 + [34] = '_', // "
41 + [35] = '_', // #
42 + [36] = '_', // $
43 + [37] = '_', // %
44 + [38] = '_', // &
45 + [39] = '_', // '
46 + [40] = '_', // (
47 + [41] = '_', // )
48 + [42] = '_', // *
49 + [43] = '_', // +
50 + [44] = '.', // ,
51 + [45] = '-', // -
52 + [46] = '.', // .
53 + [47] = '/', // /
54 + [48] = '0', // 0
55 + [49] = '1', // 1
56 + [50] = '2', // 2
57 + [51] = '3', // 3
58 + [52] = '4', // 4
59 + [53] = '5', // 5
60 + [54] = '6', // 6
61 + [55] = '7', // 7
62 + [56] = '8', // 8
63 + [57] = '9', // 9
64 + [58] = '_', // :
65 + [59] = '_', // ;
66 + [60] = '_', // <
67 + [61] = '_', // =
68 + [62] = '_', // >
69 + [63] = '_', // ?
70 + [64] = '_', // @
71 + [65] = 'a', // A
72 + [66] = 'b', // B
73 + [67] = 'c', // C
74 + [68] = 'd', // D
75 + [69] = 'e', // E
76 + [70] = 'f', // F
77 + [71] = 'g', // G
78 + [72] = 'h', // H
79 + [73] = 'i', // I
80 + [74] = 'j', // J
81 + [75] = 'k', // K
82 + [76] = 'l', // L
83 + [77] = 'm', // M
84 + [78] = 'n', // N
85 + [79] = 'o', // O
86 + [80] = 'p', // P
87 + [81] = 'q', // Q
88 + [82] = 'r', // R
89 + [83] = 's', // S
90 + [84] = 't', // T
91 + [85] = 'u', // U
92 + [86] = 'v', // V
93 + [87] = 'w', // W
94 + [88] = 'x', // X
95 + [89] = 'y', // Y
96 + [90] = 'z', // Z
97 + [91] = '_', // [
98 + [92] = '/', // backslash
99 + [93] = '_', // ]
100 + [94] = '_', // ^
101 + [95] = '_', // _
102 + [96] = '_', // `
103 + [97] = 'a', // a
104 + [98] = 'b', // b
105 + [99] = 'c', // c
106 + [100] = 'd', // d
107 + [101] = 'e', // e
108 + [102] = 'f', // f
109 + [103] = 'g', // g
110 + [104] = 'h', // h
111 + [105] = 'i', // i
112 + [106] = 'j', // j
113 + [107] = 'k', // k
114 + [108] = 'l', // l
115 + [109] = 'm', // m
116 + [110] = 'n', // n
117 + [111] = 'o', // o
118 + [112] = 'p', // p
119 + [113] = 'q', // q
120 + [114] = 'r', // r
121 + [115] = 's', // s
122 + [116] = 't', // t
123 + [117] = 'u', // u
124 + [118] = 'v', // v
125 + [119] = 'w', // w
126 + [120] = 'x', // x
127 + [121] = 'y', // y
128 + [122] = 'z', // z
129 + [123] = '_', // {
130 + [124] = '_', // |
131 + [125] = '_', // }
132 + [126] = '_', // ~
133 + [127] = '_', //
134 + [128] = '_', //
135 + [129] = '_', //
136 + [130] = '_', //
137 + [131] = '_', //
138 + [132] = '_', //
139 + [133] = '_', //
140 + [134] = '_', //
141 + [135] = '_', //
142 + [136] = '_', //
143 + [137] = '_', //
144 + [138] = '_', //
145 + [139] = '_', //
146 + [140] = '_', //
147 + [141] = '_', //
148 + [142] = '_', //
149 + [143] = '_', //
150 + [144] = '_', //
151 + [145] = '_', //
152 + [146] = '_', //
153 + [147] = '_', //
154 + [148] = '_', //
155 + [149] = '_', //
156 + [150] = '_', //
157 + [151] = '_', //
158 + [152] = '_', //
159 + [153] = '_', //
160 + [154] = '_', //
161 + [155] = '_', //
162 + [156] = '_', //
163 + [157] = '_', //
164 + [158] = '_', //
165 + [159] = '_', //
166 + [160] = '_', //
167 + [161] = '_', //
168 + [162] = '_', //
169 + [163] = '_', //
170 + [164] = '_', //
171 + [165] = '_', //
172 + [166] = '_', //
173 + [167] = '_', //
174 + [168] = '_', //
175 + [169] = '_', //
176 + [170] = '_', //
177 + [171] = '_', //
178 + [172] = '_', //
179 + [173] = '_', //
180 + [174] = '_', //
181 + [175] = '_', //
182 + [176] = '_', //
183 + [177] = '_', //
184 + [178] = '_', //
185 + [179] = '_', //
186 + [180] = '_', //
187 + [181] = '_', //
188 + [182] = '_', //
189 + [183] = '_', //
190 + [184] = '_', //
191 + [185] = '_', //
192 + [186] = '_', //
193 + [187] = '_', //
194 + [188] = '_', //
195 + [189] = '_', //
196 + [190] = '_', //
197 + [191] = '_', //
198 + [192] = '_', //
199 + [193] = '_', //
200 + [194] = '_', //
201 + [195] = '_', //
202 + [196] = '_', //
203 + [197] = '_', //
204 + [198] = '_', //
205 + [199] = '_', //
206 + [200] = '_', //
207 + [201] = '_', //
208 + [202] = '_', //
209 + [203] = '_', //
210 + [204] = '_', //
211 + [205] = '_', //
212 + [206] = '_', //
213 + [207] = '_', //
214 + [208] = '_', //
215 + [209] = '_', //
216 + [210] = '_', //
217 + [211] = '_', //
218 + [212] = '_', //
219 + [213] = '_', //
220 + [214] = '_', //
221 + [215] = '_', //
222 + [216] = '_', //
223 + [217] = '_', //
224 + [218] = '_', //
225 + [219] = '_', //
226 + [220] = '_', //
227 + [221] = '_', //
228 + [222] = '_', //
229 + [223] = '_', //
230 + [224] = '_', //
231 + [225] = '_', //
232 + [226] = '_', //
233 + [227] = '_', //
234 + [228] = '_', //
235 + [229] = '_', //
236 + [230] = '_', //
237 + [231] = '_', //
238 + [232] = '_', //
239 + [233] = '_', //
240 + [234] = '_', //
241 + [235] = '_', //
242 + [236] = '_', //
243 + [237] = '_', //
244 + [238] = '_', //
245 + [239] = '_', //
246 + [240] = '_', //
247 + [241] = '_', //
248 + [242] = '_', //
249 + [243] = '_', //
250 + [244] = '_', //
251 + [245] = '_', //
252 + [246] = '_', //
253 + [247] = '_', //
254 + [248] = '_', //
255 + [249] = '_', //
256 + [250] = '_', //
257 + [251] = '_', //
258 + [252] = '_', //
259 + [253] = '_', //
260 + [254] = '_', //
261 + [255] = '_' //
262 +};
263 +
264 +// make sure the supplied string
265 +// is good for a netdata chart/dimension ID/NAME
266 +void netdata_fix_chart_name(char *s) {
267 + while ((*s = netdata_map_chart_names[(uint8_t)*s])) s++;
268 +}
269 +
270 +static uint8_t netdata_map_chart_ids[256] = {
271 + [0] = '\0', //
272 + [1] = '_', //
273 + [2] = '_', //
274 + [3] = '_', //
275 + [4] = '_', //
276 + [5] = '_', //
277 + [6] = '_', //
278 + [7] = '_', //
279 + [8] = '_', //
280 + [9] = '_', //
281 + [10] = '_', //
282 + [11] = '_', //
283 + [12] = '_', //
284 + [13] = '_', //
285 + [14] = '_', //
286 + [15] = '_', //
287 + [16] = '_', //
288 + [17] = '_', //
289 + [18] = '_', //
290 + [19] = '_', //
291 + [20] = '_', //
292 + [21] = '_', //
293 + [22] = '_', //
294 + [23] = '_', //
295 + [24] = '_', //
296 + [25] = '_', //
297 + [26] = '_', //
298 + [27] = '_', //
299 + [28] = '_', //
300 + [29] = '_', //
301 + [30] = '_', //
302 + [31] = '_', //
303 + [32] = '_', //
304 + [33] = '_', // !
305 + [34] = '_', // "
306 + [35] = '_', // #
307 + [36] = '_', // $
308 + [37] = '_', // %
309 + [38] = '_', // &
310 + [39] = '_', // '
311 + [40] = '_', // (
312 + [41] = '_', // )
313 + [42] = '_', // *
314 + [43] = '_', // +
315 + [44] = '.', // ,
316 + [45] = '-', // -
317 + [46] = '.', // .
318 + [47] = '_', // /
319 + [48] = '0', // 0
320 + [49] = '1', // 1
321 + [50] = '2', // 2
322 + [51] = '3', // 3
323 + [52] = '4', // 4
324 + [53] = '5', // 5
325 + [54] = '6', // 6
326 + [55] = '7', // 7
327 + [56] = '8', // 8
328 + [57] = '9', // 9
329 + [58] = '_', // :
330 + [59] = '_', // ;
331 + [60] = '_', // <
332 + [61] = '_', // =
333 + [62] = '_', // >
334 + [63] = '_', // ?
335 + [64] = '_', // @
336 + [65] = 'a', // A
337 + [66] = 'b', // B
338 + [67] = 'c', // C
339 + [68] = 'd', // D
340 + [69] = 'e', // E
341 + [70] = 'f', // F
342 + [71] = 'g', // G
343 + [72] = 'h', // H
344 + [73] = 'i', // I
345 + [74] = 'j', // J
346 + [75] = 'k', // K
347 + [76] = 'l', // L
348 + [77] = 'm', // M
349 + [78] = 'n', // N
350 + [79] = 'o', // O
351 + [80] = 'p', // P
352 + [81] = 'q', // Q
353 + [82] = 'r', // R
354 + [83] = 's', // S
355 + [84] = 't', // T
356 + [85] = 'u', // U
357 + [86] = 'v', // V
358 + [87] = 'w', // W
359 + [88] = 'x', // X
360 + [89] = 'y', // Y
361 + [90] = 'z', // Z
362 + [91] = '_', // [
363 + [92] = '_', // backslash
364 + [93] = '_', // ]
365 + [94] = '_', // ^
366 + [95] = '_', // _
367 + [96] = '_', // `
368 + [97] = 'a', // a
369 + [98] = 'b', // b
370 + [99] = 'c', // c
371 + [100] = 'd', // d
372 + [101] = 'e', // e
373 + [102] = 'f', // f
374 + [103] = 'g', // g
375 + [104] = 'h', // h
376 + [105] = 'i', // i
377 + [106] = 'j', // j
378 + [107] = 'k', // k
379 + [108] = 'l', // l
380 + [109] = 'm', // m
381 + [110] = 'n', // n
382 + [111] = 'o', // o
383 + [112] = 'p', // p
384 + [113] = 'q', // q
385 + [114] = 'r', // r
386 + [115] = 's', // s
387 + [116] = 't', // t
388 + [117] = 'u', // u
389 + [118] = 'v', // v
390 + [119] = 'w', // w
391 + [120] = 'x', // x
392 + [121] = 'y', // y
393 + [122] = 'z', // z
394 + [123] = '_', // {
395 + [124] = '_', // |
396 + [125] = '_', // }
397 + [126] = '_', // ~
398 + [127] = '_', //
399 + [128] = '_', //
400 + [129] = '_', //
401 + [130] = '_', //
402 + [131] = '_', //
403 + [132] = '_', //
404 + [133] = '_', //
405 + [134] = '_', //
406 + [135] = '_', //
407 + [136] = '_', //
408 + [137] = '_', //
409 + [138] = '_', //
410 + [139] = '_', //
411 + [140] = '_', //
412 + [141] = '_', //
413 + [142] = '_', //
414 + [143] = '_', //
415 + [144] = '_', //
416 + [145] = '_', //
417 + [146] = '_', //
418 + [147] = '_', //
419 + [148] = '_', //
420 + [149] = '_', //
421 + [150] = '_', //
422 + [151] = '_', //
423 + [152] = '_', //
424 + [153] = '_', //
425 + [154] = '_', //
426 + [155] = '_', //
427 + [156] = '_', //
428 + [157] = '_', //
429 + [158] = '_', //
430 + [159] = '_', //
431 + [160] = '_', //
432 + [161] = '_', //
433 + [162] = '_', //
434 + [163] = '_', //
435 + [164] = '_', //
436 + [165] = '_', //
437 + [166] = '_', //
438 + [167] = '_', //
439 + [168] = '_', //
440 + [169] = '_', //
441 + [170] = '_', //
442 + [171] = '_', //
443 + [172] = '_', //
444 + [173] = '_', //
445 + [174] = '_', //
446 + [175] = '_', //
447 + [176] = '_', //
448 + [177] = '_', //
449 + [178] = '_', //
450 + [179] = '_', //
451 + [180] = '_', //
452 + [181] = '_', //
453 + [182] = '_', //
454 + [183] = '_', //
455 + [184] = '_', //
456 + [185] = '_', //
457 + [186] = '_', //
458 + [187] = '_', //
459 + [188] = '_', //
460 + [189] = '_', //
461 + [190] = '_', //
462 + [191] = '_', //
463 + [192] = '_', //
464 + [193] = '_', //
465 + [194] = '_', //
466 + [195] = '_', //
467 + [196] = '_', //
468 + [197] = '_', //
469 + [198] = '_', //
470 + [199] = '_', //
471 + [200] = '_', //
472 + [201] = '_', //
473 + [202] = '_', //
474 + [203] = '_', //
475 + [204] = '_', //
476 + [205] = '_', //
477 + [206] = '_', //
478 + [207] = '_', //
479 + [208] = '_', //
480 + [209] = '_', //
481 + [210] = '_', //
482 + [211] = '_', //
483 + [212] = '_', //
484 + [213] = '_', //
485 + [214] = '_', //
486 + [215] = '_', //
487 + [216] = '_', //
488 + [217] = '_', //
489 + [218] = '_', //
490 + [219] = '_', //
491 + [220] = '_', //
492 + [221] = '_', //
493 + [222] = '_', //
494 + [223] = '_', //
495 + [224] = '_', //
496 + [225] = '_', //
497 + [226] = '_', //
498 + [227] = '_', //
499 + [228] = '_', //
500 + [229] = '_', //
501 + [230] = '_', //
502 + [231] = '_', //
503 + [232] = '_', //
504 + [233] = '_', //
505 + [234] = '_', //
506 + [235] = '_', //
507 + [236] = '_', //
508 + [237] = '_', //
509 + [238] = '_', //
510 + [239] = '_', //
511 + [240] = '_', //
512 + [241] = '_', //
513 + [242] = '_', //
514 + [243] = '_', //
515 + [244] = '_', //
516 + [245] = '_', //
517 + [246] = '_', //
518 + [247] = '_', //
519 + [248] = '_', //
520 + [249] = '_', //
521 + [250] = '_', //
522 + [251] = '_', //
523 + [252] = '_', //
524 + [253] = '_', //
525 + [254] = '_', //
526 + [255] = '_' //
527 +};
528 +
529 +// make sure the supplied string
530 +// is good for a netdata chart/dimension ID/NAME
531 +void netdata_fix_chart_id(char *s) {
532 + while ((*s = netdata_map_chart_ids[(uint8_t) *s])) s++;
533 +}
534 +
535 +// --------------------------------------------------------------------------------------------------------------------
536 +
537 +/*
538 + * Sanitize Chart Meta
539 + *
540 + * We should only remove characters that somehow influence the functionality of Netdata.
541 + *
542 + * The following ASCII characters are replaced with underscores:
543 + *
544 + * comma and pipe Used in HTTP GET as list separators
545 + * colon Used in Labels as value list separators
546 + * space Used in HTTP GET as parameter separators
547 + * asterisk Used in simple patterns as wildcard
548 + * exclamation mark Used in simple patterns for negative matches
549 + * Control characters and not-printable characters (<= ' ')
550 + *
551 + * Non-ASCII UTF8 characters are retained as-is.
552 + *
553 + * When multiple consecutive characters are converted to underscores
554 + * only 1 underscore is copied to the output buffer.
555 + *
556 + */
557 +void sanitize_chart_meta(char *buf) {
558 + char *s = buf, *d = buf;
559 + bool last_was_underscore = false;
560 +
561 + // Process the input buffer
562 + while (*s) {
563 + if (!IS_UTF8_BYTE(*s)) { // ASCII character
564 + if (*s == '_') {
565 + if(!last_was_underscore)
566 + *d++ = *s++;
567 + else
568 + s++;
569 +
570 + last_was_underscore = true;
571 + }
572 + else if (*s <= ' ' || // all non-printable and control characters, including the space
573 + *s == ',' || // HTTP GET list separator
574 + *s == ':' || // labels value list separator
575 + *s == '|' || // HTTP GET list separator
576 + *s == '*' || // simple pattern wildcard
577 + *s == '!' // simple pattern negative match
578 + ) {
579 + if (!last_was_underscore)
580 + *d++ = '_';
581 +
582 + s++;
583 + last_was_underscore = true;
584 + }
585 + else {
586 + *d++ = *s++;
587 + last_was_underscore = false;
588 + }
589 + }
590 + else if (IS_UTF8_STARTBYTE(*s)) {
591 + // copy the start byte
592 + *d++ = *s++;
593 +
594 + // copy the rest of the wide character
595 + while (IS_UTF8_CONTBYTE(*s))
596 + *d++ = *s++;
597 +
598 + last_was_underscore = false;
599 + }
600 + else
601 + // an invalid UTF8 continuation byte - skip it
602 + s++;
603 + }
604 +
605 + // Null-terminate the cleaned string
606 + *d = '\0';
607 +
608 + // Remove a trailing dash (if any)
609 + if (d > buf && *(d - 1) == '_')
610 + *(d - 1) = '\0';
611 +}
612 +
613 +STRING *sanitize_chart_meta_string(STRING *s) {
614 + char buf[string_strlen(s) + 1];
615 + memcpy(buf, string2str(s), sizeof(buf));
616 + sanitize_chart_meta(buf);
617 + return string_strdupz(buf);
618 +}
src/libnetdata/sanitizers/chart_id_and_name.h new
+14
@@ -0,0 +1,14 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_CHART_ID_AND_NAME_H
4 +#define NETDATA_CHART_ID_AND_NAME_H
5 +
6 +#include "../libnetdata.h"
7 +
8 +void netdata_fix_chart_id(char *s);
9 +void netdata_fix_chart_name(char *s);
10 +
11 +void sanitize_chart_meta(char *buf);
12 +STRING *sanitize_chart_meta_string(STRING *s);
13 +
14 +#endif //NETDATA_CHART_ID_AND_NAME_H
src/libnetdata/string/string.c
+20 -2
@@ -347,16 +347,34 @@ void string_freez(STRING *string) {
347 string_stats_atomic_increment(partition, releases);
348 }
349
350 -inline size_t string_strlen(STRING *string) {
350 +inline size_t string_strlen(const STRING *string) {
351 if(unlikely(!string)) return 0;
352 return string->length - 1;
353 }
354
355 -inline const char *string2str(STRING *string) {
355 +inline const char *string2str(const STRING *string) {
356 if(unlikely(!string)) return "";
357 return string->str;
358 }
359
360 +bool string_ends_with_string(const STRING *whole, const STRING *end) {
361 + if(whole == end) return true;
362 + if(!whole || !end) return false;
363 + if(end->length > whole->length) return false;
364 + if(end->length == whole->length) return strcmp(string2str(whole), string2str(end)) == 0;
365 + const char *we = string2str(whole);
366 + we = &we[string_strlen(whole) - string_strlen(end)];
367 + return strncmp(we, end->str, string_strlen(end)) == 0;
368 +}
369 +
370 +bool string_starts_with_string(const STRING *whole, const STRING *end) {
371 + if(whole == end) return true;
372 + if(!whole || !end) return false;
373 + if(end->length > whole->length) return false;
374 + if(end->length == whole->length) return strcmp(string2str(whole), string2str(end)) == 0;
375 + return strncmp(string2str(whole), string2str(end), string_strlen(end)) == 0;
376 +}
377 +
378 STRING *string_2way_merge(STRING *a, STRING *b) {
379 static STRING *X = NULL;
380
src/libnetdata/string/string.h
+4 -2
@@ -14,8 +14,10 @@ STRING *string_strndupz(const char *str, size_t len);
14
15 STRING *string_dup(STRING *string);
16 void string_freez(STRING *string);
17 -size_t string_strlen(STRING *string);
18 -const char *string2str(STRING *string) NEVERNULL;
17 +size_t string_strlen(const STRING *string);
18 +const char *string2str(const STRING *string) NEVERNULL;
19 +bool string_ends_with_string(const STRING *whole, const STRING *end);
20 +bool string_starts_with_string(const STRING *whole, const STRING *end);
21
22 // keep common prefix/suffix and replace everything else with [x]
23 STRING *string_2way_merge(STRING *a, STRING *b);
src/libnetdata/string/utf8.h
+3 -2
@@ -3,7 +3,8 @@
3 #ifndef NETDATA_STRING_UTF8_H
4 #define NETDATA_STRING_UTF8_H 1
5
6 -#define IS_UTF8_BYTE(x) ((x) & 0x80)
7 -#define IS_UTF8_STARTBYTE(x) (IS_UTF8_BYTE(x)&&((x) & 0x40))
6 +#define IS_UTF8_BYTE(x) ((uint8_t)(x) & (uint8_t)0x80)
7 +#define IS_UTF8_STARTBYTE(x) (IS_UTF8_BYTE(x) && ((uint8_t)(x) & (uint8_t)0x40))
8 +#define IS_UTF8_CONTBYTE(x) (IS_UTF8_BYTE(x) && !IS_UTF8_STARTBYTE(x))
9
10 #endif /* NETDATA_STRING_UTF8_H */