1 /*
2 * libstatgrab
3 * https://libstatgrab.org
4 * Copyright (C) 2003-2004 Peter Saunders
5 * Copyright (C) 2003-2019 Tim Bishop
6 * Copyright (C) 2003-2013 Adam Sampson
7 * Copyright (C) 2012-2019 Jens Rehsack
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 * 02110-1301, USA.
23 */
24
25 #include "tools.h"
26 #ifdef WIN32
27 #include <pdhmsg.h>
28
29 static HQUERY h_query;
30
31 static HCOUNTER current_han[SG_WIN32_SIZE];
32
33 static char **diskio_names;
34 static HCOUNTER *diskio_rhan;
35 static HCOUNTER *diskio_whan;
36 static int diskio_no;
37
38 static int is_started = 0;
39
40 /*static char **netio_names;
41 static HCOUNTER *netio_rhan;
42 static HCOUNTER *netio_shan;
43 static int netio_no;*/
44
add_counter(const char * fullCounterPath,HCOUNTER * phCounter)45 int add_counter(const char *fullCounterPath, HCOUNTER *phCounter)
46 {
47 PDH_STATUS pdh_status;
48
49 pdh_status = PdhAddCounter(h_query, fullCounterPath,
50 0, phCounter);
51 if(pdh_status != ERROR_SUCCESS) {
52 //sg_set_error(SG_ERROR_PDHADD, fullCounterPath);
53 phCounter = NULL;
54 return -1;
55 }
56 return 0;
57 }
58
read_counter_double(pdh_enum counter,double * result)59 int read_counter_double(pdh_enum counter, double *result)
60 {
61 PDH_STATUS pdh_status;
62 PDH_FMT_COUNTERVALUE *item_buf;
63 HCOUNTER hcounter = current_han[counter];
64
65 if(hcounter == NULL)
66 return -1;
67
68 item_buf = sg_malloc(sizeof(PDH_FMT_COUNTERVALUE));
69 if (item_buf == NULL) {
70 return -1;
71 }
72
73 pdh_status = PdhGetFormattedCounterValue(hcounter, PDH_FMT_DOUBLE, NULL,
74 item_buf);
75 if(pdh_status != ERROR_SUCCESS) {
76 free(item_buf);
77 return -1;
78 }
79 *result = item_buf->doubleValue;
80 free(item_buf);
81 return 0;
82 }
83
read_counter_large_int(HCOUNTER hcounter,long long * result)84 static int read_counter_large_int(HCOUNTER hcounter, long long *result)
85 {
86 PDH_STATUS pdh_status;
87 PDH_FMT_COUNTERVALUE *item_buf;
88
89 if(hcounter == NULL)
90 return -1;
91
92 item_buf = sg_malloc(sizeof(PDH_FMT_COUNTERVALUE));
93 if (item_buf == NULL) {
94 return -1;
95 }
96
97 pdh_status = PdhGetFormattedCounterValue(hcounter, PDH_FMT_LARGE, NULL,
98 item_buf);
99 if(pdh_status != ERROR_SUCCESS) {
100 free(item_buf);
101 /*switch(pdh_status) {
102 case PDH_INVALID_ARGUMENT:
103 printf("invalid argument\n");
104 break;
105 case PDH_INVALID_DATA:
106 printf("invalid data\n");
107 break;
108 case PDH_INVALID_HANDLE:
109 printf("invalid handle\n");
110 break;
111 }*/
112 return -1;
113 }
114 *result = item_buf->largeValue;
115 free(item_buf);
116 return 0;
117 }
118
read_counter_large(pdh_enum counter,long long * result)119 int read_counter_large(pdh_enum counter, long long *result)
120 {
121 return read_counter_large_int(current_han[counter], result);
122 }
123
get_instances(const char * object)124 static char *get_instances(const char *object)
125 {
126 PDH_STATUS pdh_status;
127 char *instancelistbuf;
128 DWORD instancelistsize = 0;
129 char *counterlistbuf;
130 DWORD counterlistsize = 0;
131
132 /* Get necessary size of buffers */
133 pdh_status = PdhEnumObjectItems(NULL, NULL, object, NULL,
134 &counterlistsize, NULL, &instancelistsize,
135 PERF_DETAIL_WIZARD, 0);
136 /* 2k is dodgy and returns ERROR_SUCCESS even though the buffers were
137 * NULL */
138 if(pdh_status == PDH_MORE_DATA || pdh_status == ERROR_SUCCESS) {
139 instancelistbuf = sg_malloc(instancelistsize * sizeof(TCHAR));
140 counterlistbuf = sg_malloc(counterlistsize * sizeof(TCHAR));
141 if (instancelistbuf != NULL && counterlistbuf != NULL) {
142 pdh_status = PdhEnumObjectItems(NULL, NULL, object,
143 counterlistbuf, &counterlistsize,
144 instancelistbuf, &instancelistsize,
145 PERF_DETAIL_WIZARD, 0);
146 if (pdh_status == ERROR_SUCCESS) {
147 free(counterlistbuf);
148 return instancelistbuf;
149 }
150 }
151 if (counterlistbuf != NULL)
152 free(counterlistbuf);
153 if(instancelistbuf != NULL)
154 free(instancelistbuf);
155 }
156 return NULL;
157 }
158
159 /* Gets the instance buffer. Removes _Total item. Works out how many items
160 * there are. Returns these in a pointer list, rather than single buffer.
161 */
get_instance_list(const char * object,int * n)162 static char **get_instance_list(const char *object, int *n)
163 {
164 char *thisinstance = NULL;
165 char *instances = NULL;
166 char **list;
167 char **listtmp = NULL;
168 int i;
169 *n = 0;
170
171 instances = get_instances(object);
172 if (instances == NULL)
173 return NULL;
174
175 list = (char **)sg_malloc(sizeof(char *));
176 if (list == NULL) {
177 return NULL;
178 }
179 for (thisinstance = instances; *thisinstance != 0;
180 thisinstance += strlen(thisinstance) + 1) {
181 /* Skip over the _Total item */
182 if (strcmp(thisinstance,"_Total") == 0) continue;
183
184 listtmp = (char **)sg_realloc(list, sizeof(char *) * ((*n)+1));
185 if (listtmp == NULL) {
186 goto out;
187 }
188 list = listtmp;
189 list[*n] = sg_malloc(strlen(thisinstance) +1);
190 if(list[*n] == NULL) {
191 goto out;
192 }
193 list[*n] = strcpy(list[*n], thisinstance);
194 ++*n;
195 }
196 free (instances);
197 return list;
198
199 out:
200 for (i = 0; i < *n; i++) {
201 free(list[i]);
202 }
203 free(list);
204 return NULL;
205 }
206
get_diskio(const int no,long long * read,long long * write)207 char *get_diskio(const int no, long long *read, long long *write)
208 {
209 int result;
210 char *name = NULL;
211
212 if (no >= diskio_no || no < 0)
213 return NULL;
214
215 result = read_counter_large_int(diskio_rhan[no], read);
216 result = result + read_counter_large_int(diskio_whan[no], write);
217 if (result) {
218 sg_set_error_fmt(SG_ERROR_PDHREAD, "diskio");
219 return NULL;
220 }
221 if (sg_update_string(&name, diskio_names[no]))
222 return NULL;
223 return name;
224 }
225
226 /* We do not fail on add_counter here, as some counters may not exist on all
227 * hosts. The rest will work, and the missing/failed counters will die nicely
228 * elsewhere */
add_all_monitors()229 static int add_all_monitors()
230 {
231 int i;
232 char tmp[512];
233
234 add_counter(PDH_USER, &(current_han[SG_WIN32_PROC_USER]));
235 add_counter(PDH_PRIV, &(current_han[SG_WIN32_PROC_PRIV]));
236 add_counter(PDH_IDLE, &(current_han[SG_WIN32_PROC_IDLE]));
237 add_counter(PDH_INTER, &(current_han[SG_WIN32_PROC_INT]));
238 add_counter(PDH_MEM_CACHE, &(current_han[SG_WIN32_MEM_CACHE]));
239 add_counter(PDH_UPTIME, &(current_han[SG_WIN32_UPTIME]));
240 add_counter(PDH_PAGEIN, &(current_han[SG_WIN32_PAGEIN]));
241 add_counter(PDH_PAGEOUT, &(current_han[SG_WIN32_PAGEOUT]));
242
243 diskio_names = get_instance_list("PhysicalDisk", &diskio_no);
244 if (diskio_names != NULL) {
245 diskio_rhan = (HCOUNTER *)malloc(sizeof(HCOUNTER) * diskio_no);
246 if (diskio_rhan == NULL) {
247 PdhCloseQuery(h_query);
248 return -1;
249 }
250 diskio_whan = (HCOUNTER *)malloc(sizeof(HCOUNTER) * diskio_no);
251 if (diskio_whan == NULL) {
252 PdhCloseQuery(h_query);
253 free (diskio_rhan);
254 return -1;
255 }
256 for (i = 0; i < diskio_no; i++) {
257 snprintf(tmp, sizeof(tmp), PDH_DISKIOREAD, diskio_names[i]);
258 add_counter(tmp, &diskio_rhan[i]);
259
260 snprintf(tmp, sizeof(tmp), PDH_DISKIOWRITE, diskio_names[i]);
261 add_counter(tmp, &diskio_whan[i]);
262 }
263 }
264 return 0;
265 }
266 #endif
267
268 /* Call before trying to get search results back, otherwise it'll be dead data
269 */
sg_win32_snapshot()270 int sg_win32_snapshot()
271 {
272 #ifdef WIN32
273 PDH_STATUS pdh_status;
274
275 if(!is_started) {
276 return -1;
277 }
278
279 pdh_status = PdhCollectQueryData(h_query);
280 if(pdh_status != ERROR_SUCCESS) {
281 sg_set_error_fmt(SG_ERROR_PDHCOLLECT, NULL);
282 return -1;
283 }
284 #endif
285 return 0;
286 }
287
288 /* Must be called before any values can be read. This creates all the
289 * necessary PDH values
290 */
sg_win32_start_capture()291 int sg_win32_start_capture()
292 {
293 #ifdef WIN32
294 PDH_STATUS pdh_status;
295
296 if(is_started) {
297 return -1;
298 }
299
300 pdh_status = PdhOpenQuery(NULL, 0, &h_query);
301
302 if(pdh_status != ERROR_SUCCESS) {
303 char *mess = NULL;
304 if(pdh_status == PDH_INVALID_ARGUMENT)
305 mess = "Invalid argument o.O";
306 else if(pdh_status == PDH_MEMORY_ALLOCATION_FAILURE)
307 mess = "Memory allocation failure";
308 sg_set_error_fmt(SG_ERROR_PDHOPEN, mess);
309 return -1;
310 }
311 if (add_all_monitors() == -1) {
312 return -1;
313 }
314
315 is_started = 1;
316 #endif
317 return 0;
318 }
319
320 /* Must be called before the program exits, or to close all the capture items
321 * before opening with a call to start_capture
322 */
sg_win32_end_capture()323 void sg_win32_end_capture()
324 {
325 #ifdef WIN32
326 int i;
327 PDH_STATUS pdh_status;
328
329 if(!is_started) {
330 return;
331 }
332
333 pdh_status = PdhCloseQuery(h_query);
334 for (i=0; i < SG_WIN32_SIZE; ++i) {
335 PdhRemoveCounter(current_han[i]);
336 current_han[i] = NULL;
337 }
338 /*free_io(&diskio_no, diskio_names, diskio_rhan, diskio_whan);
339 free_io(&netio_no, netio_names, netio_rhan, netio_shan);*/
340 for (i=0; i < diskio_no; ++i) {
341 PdhRemoveCounter(diskio_rhan[i]);
342 PdhRemoveCounter(diskio_whan[i]);
343 free(diskio_names[i]);
344 }
345 free(diskio_names);
346 free(diskio_rhan);
347 free(diskio_whan);
348 diskio_no = 0;
349
350 is_started = 0;
351 #endif
352 }
353
354 #ifdef WIN32
355 // DllMain() is the entry-point function for this DLL.
356
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)357 BOOL WINAPI DllMain(HINSTANCE hinstDLL, // DLL module handle
358 DWORD fdwReason, // reason called
359 LPVOID lpvReserved) // reserved
360 {
361 switch(fdwReason)
362 {
363 case DLL_PROCESS_ATTACH:
364 if( SG_ERROR_NONE != sg_win32_start_capture() )
365 return FALSE;
366 break;
367
368 case DLL_PROCESS_DETACH:
369 sg_win32_end_capture();
370 break;
371
372 #ifdef SG_WITH_THREADS
373 case DLL_THREAD_ATTACH:
374 break;
375
376 case DLL_THREAD_DETACH:
377 sg_destroy_globals(NULL);
378 break;
379 #endif
380
381 default:
382 break;
383 }
384
385 return TRUE;
386 }
387 #endif
388