101
102 typedef struct {
103 UpdateQueryS query;
104 HCOUNTER counter;
105 bool initialized;
106 } CounterQueryS, *CounterQueryP;
107
108 typedef struct {
109 UpdateQueryS query;
110 HCOUNTER* counters;
111 int noOfCounters;
112 bool initialized;
113 } MultiCounterQueryS, *MultiCounterQueryP;
114
115 typedef struct {
116 MultiCounterQueryP queries;
117 int size;
118 bool initialized;
119 } MultiCounterQuerySetS, *MultiCounterQuerySetP;
120
121 static void pdh_cleanup(HQUERY* const query, HCOUNTER* const counter) {
122 if (counter != NULL && *counter != NULL) {
123 PdhDll::PdhRemoveCounter(*counter);
124 *counter = NULL;
125 }
126 if (query != NULL && *query != NULL) {
127 PdhDll::PdhCloseQuery(*query);
128 *query = NULL;
129 }
130 }
131
132 static CounterQueryP create_counter_query() {
133 CounterQueryP const query = NEW_C_HEAP_ARRAY(CounterQueryS, 1, mtInternal);
134 memset(query, 0, sizeof(CounterQueryS));
135 return query;
136 }
137
138 static void destroy_counter_query(CounterQueryP query) {
139 assert(query != NULL, "invariant");
140 pdh_cleanup(&query->query.query, &query->counter);
141 FREE_C_HEAP_ARRAY(CounterQueryS, query);
142 }
143
144 static MultiCounterQueryP create_multi_counter_query() {
145 MultiCounterQueryP const query = NEW_C_HEAP_ARRAY(MultiCounterQueryS, 1, mtInternal);
146 memset(query, 0, sizeof(MultiCounterQueryS));
147 return query;
148 }
149
150 static void destroy_counter_query(MultiCounterQueryP counter_query) {
151 if (counter_query != NULL) {
152 for (int i = 0; i < counter_query->noOfCounters; ++i) {
153 pdh_cleanup(NULL, &counter_query->counters[i]);
154 }
155 FREE_C_HEAP_ARRAY(char, counter_query->counters);
156 pdh_cleanup(&counter_query->query.query, NULL);
157 FREE_C_HEAP_ARRAY(MultiCounterQueryS, counter_query);
158 }
159 }
160
161 static void destroy_counter_query(MultiCounterQuerySetP counter_query_set) {
162 for (int i = 0; i < counter_query_set->size; i++) {
163 for (int j = 0; j < counter_query_set->queries[i].noOfCounters; ++j) {
164 pdh_cleanup(NULL, &counter_query_set->queries[i].counters[j]);
165 }
166 FREE_C_HEAP_ARRAY(char, counter_query_set->queries[i].counters);
167 pdh_cleanup(&counter_query_set->queries[i].query.query, NULL);
168 }
169 FREE_C_HEAP_ARRAY(MultiCounterQueryS, counter_query_set->queries);
170 FREE_C_HEAP_ARRAY(MultiCounterQuerySetS, counter_query_set);
171 }
172
173 static int open_query(HQUERY* query) {
174 return PdhDll::PdhOpenQuery(NULL, 0, query);
175 }
176
177 template <typename QueryP>
178 static int open_query(QueryP query) {
179 return open_query(&query->query);
180 }
181
182 static int allocate_counters(MultiCounterQueryP query, size_t nofCounters) {
183 assert(query != NULL, "invariant");
184 assert(!query->initialized, "invariant");
185 assert(0 == query->noOfCounters, "invariant");
186 assert(query->counters == NULL, "invariant");
187 query->counters = (HCOUNTER*)NEW_C_HEAP_ARRAY(char, nofCounters * sizeof(HCOUNTER), mtInternal);
188 if (query->counters == NULL) {
189 return OS_ERR;
190 }
191 memset(query->counters, 0, nofCounters * sizeof(HCOUNTER));
192 query->noOfCounters = (int)nofCounters;
193 return OS_OK;
194 }
195
196 static int allocate_counters(MultiCounterQuerySetP query_set, size_t nofCounters) {
197 assert(query_set != NULL, "invariant");
198 assert(!query_set->initialized, "invariant");
199 for (int i = 0; i < query_set->size; ++i) {
200 if (allocate_counters(&query_set->queries[i], nofCounters) != OS_OK) {
201 return OS_ERR;
202 }
203 }
204 return OS_OK;
205 }
206
207 static void deallocate_counters(MultiCounterQueryP query) {
208 if (query->counters != NULL) {
209 FREE_C_HEAP_ARRAY(char, query->counters);
210 query->counters = NULL;
211 query->noOfCounters = 0;
212 }
213 }
214
215 static OSReturn add_counter(UpdateQueryP query, HCOUNTER* counter, const char* path, bool first_sample_on_init) {
216 assert(query != NULL, "invariant");
217 assert(counter != NULL, "invariant");
218 assert(path != NULL, "invariant");
219 if (query->query == NULL) {
220 if (open_query(query) != ERROR_SUCCESS) {
221 return OS_ERR;
222 }
223 }
224 assert(query->query != NULL, "invariant");
225 PDH_STATUS status = PdhDll::PdhAddCounter(query->query, path, 0, counter);
226 if (PDH_CSTATUS_NO_OBJECT == status || PDH_CSTATUS_NO_COUNTER == status) {
245
246 template <typename QueryP>
247 static OSReturn add_counter(QueryP counter_query, HCOUNTER* counter, const char* path, bool first_sample_on_init) {
248 assert(counter_query != NULL, "invariant");
249 assert(counter != NULL, "invariant");
250 assert(path != NULL, "invariant");
251 return add_counter(&counter_query->query, counter, path, first_sample_on_init);
252 }
253
254 static OSReturn add_counter(CounterQueryP counter_query, const char* path, bool first_sample_on_init) {
255 if (add_counter(counter_query, &counter_query->counter, path, first_sample_on_init) != OS_OK) {
256 // performance counter might be disabled in the registry
257 return OS_ERR;
258 }
259 counter_query->initialized = true;
260 return OS_OK;
261 }
262
263 static OSReturn add_process_counter(MultiCounterQueryP query, int slot_index, const char* path, bool first_sample_on_init) {
264 assert(query != NULL, "invariant");
265 assert(query != NULL, "invariant");
266 assert(slot_index < query->noOfCounters, "invariant");
267 assert(query->counters[slot_index] == NULL, "invariant");
268 const OSReturn ret = add_counter(query, &query->counters[slot_index], path, first_sample_on_init);
269 if (OS_OK == ret) {
270 if (slot_index + 1 == query->noOfCounters) {
271 query->initialized = true;
272 }
273 }
274 return ret;
275 }
276
277 static int collect_query_data(UpdateQueryP update_query) {
278 assert(update_query != NULL, "invariant");
279 const s8 now = os::javaTimeMillis();
280 if (now - update_query->lastUpdate > min_update_interval_millis) {
281 if (PdhDll::PdhCollectQueryData(update_query->query) != ERROR_SUCCESS) {
282 return OS_ERR;
283 }
284 update_query->lastUpdate = now;
285 }
309 * The really bad part is that this list is reset as soon as a process exits:
310 * If \Process(java#1) exits, \Process(java#3) now becomes \Process(java#2) etc.
311 *
312 * The PDH api requires a process identifier to be submitted when registering
313 * a query, but as soon as the list resets, the query is invalidated (since the name changed).
314 *
315 * Solution:
316 * The #number identifier for a Process query can only decrease after process creation.
317 *
318 * We therefore create an array of counter queries for all process object instances
319 * up to and including ourselves:
320 *
321 * Ex. we come in as third process instance (java#2), we then create and register
322 * queries for the following Process object instances:
323 * java#0, java#1, java#2
324 *
325 * current_query_index_for_process() keeps track of the current "correct" query
326 * (in order to keep this index valid when the list resets from underneath,
327 * ensure to call current_query_index_for_process() before every query involving
328 * Process object instance data).
329 */
330 static int current_query_index_for_process() {
331 assert(process_image_name != NULL, "invariant");
332 assert(pdh_IDProcess_counter_fmt != NULL, "invariant");
333 HQUERY tmpQuery = NULL;
334 if (open_query(&tmpQuery) != ERROR_SUCCESS) {
335 return 0;
336 }
337 char counter[512];
338 HCOUNTER handle_counter = NULL;
339 // iterate over all instance indexes and try to find our own pid
340 for (int index = 0; index < max_intx; index++) {
341 jio_snprintf(counter, sizeof(counter) - 1, pdh_IDProcess_counter_fmt, index);
342 assert(strlen(counter) < sizeof(counter), "invariant");
343 if (PdhDll::PdhAddCounter(tmpQuery, counter, 0, &handle_counter) != ERROR_SUCCESS) {
344 pdh_cleanup(&tmpQuery, &handle_counter);
345 return 0;
346 }
347 const PDH_STATUS res = PdhDll::PdhCollectQueryData(tmpQuery);
348 if (res == PDH_INVALID_HANDLE || res == PDH_NO_DATA) {
349 pdh_cleanup(&tmpQuery, &handle_counter);
350 return 0;
351 } else {
352 PDH_FMT_COUNTERVALUE counter_value;
353 formatted_counter_value(handle_counter, PDH_FMT_LONG, &counter_value);
354 pdh_cleanup(NULL, &handle_counter);
355 if ((LONG)os::current_process_id() == counter_value.longValue) {
356 pdh_cleanup(&tmpQuery, NULL);
357 return index;
358 }
359 }
360 }
361 pdh_cleanup(&tmpQuery, NULL);
362 return 0;
363 }
364
365 static MultiCounterQuerySetP create_process_counter_query() {
366 MultiCounterQuerySetP const query = NEW_C_HEAP_ARRAY(MultiCounterQuerySetS, 1, mtInternal);
367 memset(query, 0, sizeof(MultiCounterQuerySetS));
368 const int current_process_idx = current_query_index_for_process();
369 query->queries = NEW_C_HEAP_ARRAY(MultiCounterQueryS, current_process_idx + 1, mtInternal);
370 memset(query->queries, 0, sizeof(MultiCounterQueryS) * (current_process_idx + 1));
371 query->size = current_process_idx + 1;
372 return query;
373 }
374
375 static MultiCounterQueryP current_process_counter_query(MultiCounterQuerySetP process_query_set) {
376 assert(process_query_set != NULL, "invariant");
377 const int current_query_index = current_query_index_for_process();
378 assert(current_query_index < process_query_set->size, "invariant");
379 return &process_query_set->queries[current_query_index];
380 }
381
382 static void clear_multi_counter(MultiCounterQueryP query) {
383 for (int i = 0; i < query->noOfCounters; ++i) {
384 pdh_cleanup(NULL, &query->counters[i]);
385 }
386 pdh_cleanup(&query->query.query, NULL);
387 }
388
389 static int collect_process_query_data(MultiCounterQuerySetP counter_query_set) {
390 const int current_process_idx = current_query_index_for_process();
391 while (current_process_idx < counter_query_set->size - 1) {
392 const int new_size = --counter_query_set->size;
393 clear_multi_counter(&counter_query_set->queries[new_size]);
394 }
395 return collect_query_data(&counter_query_set->queries[current_process_idx]);
396 }
397
398 static int query_process_counter(MultiCounterQuerySetP process_query_set, int slot_index, DWORD format, PDH_FMT_COUNTERVALUE* const value) {
399 MultiCounterQueryP const current_query = current_process_counter_query(process_query_set);
400 assert(current_query != NULL, "invariant");
401 assert(slot_index < current_query->noOfCounters, "invariant");
402 assert(current_query->counters[slot_index] != NULL, "invariant");
403 return formatted_counter_value(current_query->counters[slot_index], format, value);
404 }
405
406 /*
407 * Construct a fully qualified PDH path
408 *
409 * @param objectName a PDH Object string representation(required)
410 * @param counterName a PDH Counter string representation(required)
411 * @param imageName a process image name string, ex. "java" (opt)
412 * @param instance an instance string, ex. "0", "1", ... (opt)
413 * @return the fully qualified PDH path.
414 *
415 * Caller will need a ResourceMark.
416 *
417 * (PdhMakeCounterPath() seems buggy on concatenating instances, hence this function instead)
418 */
419 static const char* make_fully_qualified_counter_path(const char* object_name,
793 cpu_query->initialized = true;
794 // Query once to initialize the counters which require at least two samples
795 // (like the % CPU usage) to calculate correctly.
796 collect_query_data(cpu_query);
797 return OS_OK;
798 }
799
800 static int initialize_cpu_query(MultiCounterQueryP cpu_query, DWORD pdh_counter_idx) {
801 assert(cpu_query != NULL, "invariant");
802 assert(!cpu_query->initialized, "invariant");
803 const int logical_cpu_count = number_of_logical_cpus();
804 assert(logical_cpu_count >= os::processor_count(), "invariant");
805 // we also add another counter for instance "_Total"
806 if (allocate_counters(cpu_query, logical_cpu_count + 1) != OS_OK) {
807 return OS_ERR;
808 }
809 assert(cpu_query->noOfCounters == logical_cpu_count + 1, "invariant");
810 return initialize_cpu_query_counters(cpu_query, pdh_counter_idx);
811 }
812
813 static int initialize_process_counter(MultiCounterQuerySetP query_set, int slot_index, DWORD pdh_counter_index) {
814 char* localized_process_object;
815 if (lookup_name_by_index(PDH_PROCESS_IDX, &localized_process_object) != OS_OK) {
816 return OS_ERR;
817 }
818 assert(localized_process_object != NULL, "invariant");
819 char* localized_counter_name;
820 if (lookup_name_by_index(pdh_counter_index, &localized_counter_name) != OS_OK) {
821 return OS_ERR;
822 }
823 assert(localized_counter_name != NULL, "invariant");
824 for (int i = 0; i < query_set->size; ++i) {
825 char instanceIndexBuffer[32];
826 const char* counter_path = make_fully_qualified_counter_path(localized_process_object,
827 localized_counter_name,
828 process_image_name,
829 itoa(i, instanceIndexBuffer, 10));
830 if (counter_path == NULL) {
831 return OS_ERR;
832 }
833 MultiCounterQueryP const query = &query_set->queries[i];
834 if (add_process_counter(query, slot_index, counter_path, true)) {
835 return OS_ERR;
836 }
837 }
838 return OS_OK;
839 }
840
841 static CounterQueryP create_counter_query(DWORD pdh_object_idx, DWORD pdh_counter_idx) {
842 assert(is_valid_pdh_index(pdh_object_idx), "invariant");
843 assert(is_valid_pdh_index(pdh_counter_idx), "invariant");
844 CounterQueryP const query = create_counter_query();
845 const char* object = pdh_localized_artifact(pdh_object_idx);
846 assert(object != NULL, "invariant");
847 const char* counter = pdh_localized_artifact(pdh_counter_idx);
848 assert(counter != NULL, "invariant");
849 const char* full_counter_path = make_fully_qualified_counter_path(object, counter);
850 assert(full_counter_path != NULL, "invariant");
851 add_counter(query, full_counter_path, true);
852 return query;
853 }
900 pdh_initialized = true;
901 }
902 while (InterlockedCompareExchange(&critical_section, 0, 1) == 0);
903 return ret == OS_OK;
904 }
905
906 static void pdh_release() {
907 while (InterlockedCompareExchange(&critical_section, 1, 0) == 1);
908 const LONG prev_ref_count = InterlockedExchangeAdd(&reference_count, -1);
909 if (1 == prev_ref_count) {
910 deallocate();
911 pdh_initialized = false;
912 }
913 while (InterlockedCompareExchange(&critical_section, 0, 1) == 0);
914 }
915
916 class CPUPerformanceInterface::CPUPerformance : public CHeapObj<mtInternal> {
917 friend class CPUPerformanceInterface;
918 private:
919 CounterQueryP _context_switches;
920 MultiCounterQuerySetP _process_cpu_load;
921 MultiCounterQueryP _machine_cpu_load;
922
923 int cpu_load(int which_logical_cpu, double* cpu_load);
924 int context_switch_rate(double* rate);
925 int cpu_load_total_process(double* cpu_load);
926 int cpu_loads_process(double* jvm_user_load, double* jvm_kernel_load, double* psystemTotalLoad);
927 CPUPerformance();
928 ~CPUPerformance();
929 bool initialize();
930 };
931
932 class SystemProcessInterface::SystemProcesses : public CHeapObj<mtInternal> {
933 friend class SystemProcessInterface;
934 private:
935 class ProcessIterator : public CHeapObj<mtInternal> {
936 friend class SystemProcessInterface::SystemProcesses;
937 private:
938 HANDLE _hProcessSnap;
939 PROCESSENTRY32 _pe32;
940 BOOL _valid;
952
953 ProcessIterator* _iterator;
954 SystemProcesses();
955 ~SystemProcesses();
956 bool initialize();
957
958 // information about system processes
959 int system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const;
960 };
961
962 CPUPerformanceInterface::CPUPerformance::CPUPerformance() : _context_switches(NULL), _process_cpu_load(NULL), _machine_cpu_load(NULL) {}
963
964 bool CPUPerformanceInterface::CPUPerformance::initialize() {
965 if (!pdh_acquire()) {
966 return false;
967 }
968 _context_switches = create_counter_query(PDH_SYSTEM_IDX, PDH_CONTEXT_SWITCH_RATE_IDX);
969 if (_context_switches == NULL) {
970 return false;
971 }
972 _process_cpu_load = create_process_counter_query();
973 if (_process_cpu_load == NULL) {
974 return false;
975 }
976 if (allocate_counters(_process_cpu_load, 2) != OS_OK) {
977 return false;
978 }
979 if (initialize_process_counter(_process_cpu_load, 0, PDH_PROCESSOR_TIME_IDX) != OS_OK) {
980 return false;
981 }
982 if (initialize_process_counter(_process_cpu_load, 1, PDH_PRIV_PROCESSOR_TIME_IDX) != OS_OK) {
983 return false;
984 }
985 _process_cpu_load->initialized = true;
986
987 _machine_cpu_load = create_multi_counter_query();
988 if (_machine_cpu_load == NULL) {
989 return false;
990 }
991 if (initialize_cpu_query(_machine_cpu_load, PDH_PROCESSOR_TIME_IDX) != OS_OK) {
992 return false;
993 }
994 return true;
995 }
996
997 CPUPerformanceInterface::CPUPerformance::~CPUPerformance() {
998 if (_context_switches != NULL) {
999 destroy_counter_query(_context_switches);
1000 _context_switches = NULL;
1001 }
1002 if (_process_cpu_load != NULL) {
1003 destroy_counter_query(_process_cpu_load);
1004 _process_cpu_load = NULL;
1005 }
1047 assert(_machine_cpu_load != NULL, "invariant");
1048 assert(which_logical_cpu < _machine_cpu_load->noOfCounters, "invariant");
1049 *cpu_load = .0;
1050 if (!_machine_cpu_load->initialized) {
1051 return OS_ERR;
1052 }
1053 if (collect_query_data(_machine_cpu_load)) {
1054 return OS_ERR;
1055 }
1056 // -1 is total (all cpus)
1057 const int counter_idx = -1 == which_logical_cpu ? _machine_cpu_load->noOfCounters - 1 : which_logical_cpu;
1058 PDH_FMT_COUNTERVALUE counter_value;
1059 formatted_counter_value(_machine_cpu_load->counters[counter_idx], PDH_FMT_DOUBLE, &counter_value);
1060 *cpu_load = counter_value.doubleValue / 100;
1061 return OS_OK;
1062 }
1063
1064 int CPUPerformanceInterface::CPUPerformance::cpu_load_total_process(double* cpu_load) {
1065 assert(_process_cpu_load != NULL, "invariant");
1066 *cpu_load = .0;
1067 if (!_process_cpu_load->initialized) {
1068 return OS_ERR;
1069 }
1070 if (collect_process_query_data(_process_cpu_load)) {
1071 return OS_ERR;
1072 }
1073 PDH_FMT_COUNTERVALUE counter_value;
1074 if (query_process_counter(_process_cpu_load, 0, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) {
1075 return OS_ERR;
1076 }
1077 double process_load = counter_value.doubleValue / cpu_factor();
1078 process_load = MIN2<double>(1, process_load);
1079 process_load = MAX2<double>(0, process_load);
1080 *cpu_load = process_load;
1081 return OS_OK;
1082 }
1083
1084 int CPUPerformanceInterface::CPUPerformance::cpu_loads_process(double* pjvmUserLoad,
1085 double* pjvmKernelLoad,
1086 double* psystemTotalLoad) {
1087 assert(pjvmUserLoad != NULL, "pjvmUserLoad is NULL!");
1088 assert(pjvmKernelLoad != NULL, "pjvmKernelLoad is NULL!");
1089 assert(psystemTotalLoad != NULL, "psystemTotalLoad is NULL!");
1090 *pjvmUserLoad = .0;
1091 *pjvmKernelLoad = .0;
1092 *psystemTotalLoad = .0;
1093 if (!_process_cpu_load->initialized) {
1094 return OS_ERR;
1095 }
1096 if (collect_process_query_data(_process_cpu_load)) {
1097 return OS_ERR;
1098 }
1099 double process_load = .0;
1100 PDH_FMT_COUNTERVALUE counter_value;
1101 // Read PDH_PROCESSOR_TIME_IDX
1102 if (query_process_counter(_process_cpu_load, 0, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) {
1103 return OS_ERR;
1104 }
1105 process_load = counter_value.doubleValue / cpu_factor();
1106 process_load = MIN2<double>(1, process_load);
1107 process_load = MAX2<double>(0, process_load);
1108 // Read PDH_PRIV_PROCESSOR_TIME_IDX
1109 if (query_process_counter(_process_cpu_load, 1, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) {
1110 return OS_ERR;
1111 }
1112 double kernel_load = counter_value.doubleValue / cpu_factor();
1113 kernel_load = MIN2<double>(1, kernel_load);
|
101
102 typedef struct {
103 UpdateQueryS query;
104 HCOUNTER counter;
105 bool initialized;
106 } CounterQueryS, *CounterQueryP;
107
108 typedef struct {
109 UpdateQueryS query;
110 HCOUNTER* counters;
111 int noOfCounters;
112 bool initialized;
113 } MultiCounterQueryS, *MultiCounterQueryP;
114
115 typedef struct {
116 MultiCounterQueryP queries;
117 int size;
118 bool initialized;
119 } MultiCounterQuerySetS, *MultiCounterQuerySetP;
120
121 typedef struct {
122 MultiCounterQuerySetS set;
123 int process_index;
124 } ProcessQueryS, *ProcessQueryP;
125
126 static void pdh_cleanup(HQUERY* const query, HCOUNTER* const counter) {
127 if (counter != NULL && *counter != NULL) {
128 PdhDll::PdhRemoveCounter(*counter);
129 *counter = NULL;
130 }
131 if (query != NULL && *query != NULL) {
132 PdhDll::PdhCloseQuery(*query);
133 *query = NULL;
134 }
135 }
136
137 static CounterQueryP create_counter_query() {
138 CounterQueryP const query = NEW_C_HEAP_ARRAY(CounterQueryS, 1, mtInternal);
139 memset(query, 0, sizeof(CounterQueryS));
140 return query;
141 }
142
143 static void destroy_counter_query(CounterQueryP query) {
144 assert(query != NULL, "invariant");
145 pdh_cleanup(&query->query.query, &query->counter);
146 FREE_C_HEAP_ARRAY(CounterQueryS, query);
147 }
148
149 static MultiCounterQueryP create_multi_counter_query() {
150 MultiCounterQueryP const query = NEW_C_HEAP_ARRAY(MultiCounterQueryS, 1, mtInternal);
151 memset(query, 0, sizeof(MultiCounterQueryS));
152 return query;
153 }
154
155 static void destroy_counter_query(MultiCounterQueryP counter_query) {
156 if (counter_query != NULL) {
157 for (int i = 0; i < counter_query->noOfCounters; ++i) {
158 pdh_cleanup(NULL, &counter_query->counters[i]);
159 }
160 FREE_C_HEAP_ARRAY(char, counter_query->counters);
161 pdh_cleanup(&counter_query->query.query, NULL);
162 FREE_C_HEAP_ARRAY(MultiCounterQueryS, counter_query);
163 }
164 }
165
166 static void destroy_multi_counter_query(MultiCounterQuerySetP counter_query_set) {
167 for (int i = 0; i < counter_query_set->size; i++) {
168 for (int j = 0; j < counter_query_set->queries[i].noOfCounters; ++j) {
169 pdh_cleanup(NULL, &counter_query_set->queries[i].counters[j]);
170 }
171 FREE_C_HEAP_ARRAY(char, counter_query_set->queries[i].counters);
172 pdh_cleanup(&counter_query_set->queries[i].query.query, NULL);
173 }
174 FREE_C_HEAP_ARRAY(MultiCounterQueryS, counter_query_set->queries);
175 }
176
177 static void destroy_counter_query(MultiCounterQuerySetP counter_query_set) {
178 destroy_multi_counter_query(counter_query_set);
179 FREE_C_HEAP_ARRAY(MultiCounterQuerySetS, counter_query_set);
180 }
181
182 static void destroy_counter_query(ProcessQueryP process_query) {
183 destroy_multi_counter_query(&process_query->set);
184 FREE_C_HEAP_ARRAY(ProcessQueryS, process_query);
185 }
186
187 static int open_query(HQUERY* query) {
188 return PdhDll::PdhOpenQuery(NULL, 0, query);
189 }
190
191 template <typename QueryP>
192 static int open_query(QueryP query) {
193 return open_query(&query->query);
194 }
195
196 static int allocate_counters(MultiCounterQueryP query, size_t nofCounters) {
197 assert(query != NULL, "invariant");
198 assert(!query->initialized, "invariant");
199 assert(0 == query->noOfCounters, "invariant");
200 assert(query->counters == NULL, "invariant");
201 query->counters = (HCOUNTER*)NEW_C_HEAP_ARRAY(char, nofCounters * sizeof(HCOUNTER), mtInternal);
202 if (query->counters == NULL) {
203 return OS_ERR;
204 }
205 memset(query->counters, 0, nofCounters * sizeof(HCOUNTER));
206 query->noOfCounters = (int)nofCounters;
207 return OS_OK;
208 }
209
210 static int allocate_counters(MultiCounterQuerySetP query_set, size_t nofCounters) {
211 assert(query_set != NULL, "invariant");
212 assert(!query_set->initialized, "invariant");
213 for (int i = 0; i < query_set->size; ++i) {
214 if (allocate_counters(&query_set->queries[i], nofCounters) != OS_OK) {
215 return OS_ERR;
216 }
217 }
218 return OS_OK;
219 }
220
221 static int allocate_counters(ProcessQueryP process_query, size_t nofCounters) {
222 assert(process_query != NULL, "invariant");
223 return allocate_counters(&process_query->set, nofCounters);
224 }
225
226 static void deallocate_counters(MultiCounterQueryP query) {
227 if (query->counters != NULL) {
228 FREE_C_HEAP_ARRAY(char, query->counters);
229 query->counters = NULL;
230 query->noOfCounters = 0;
231 }
232 }
233
234 static OSReturn add_counter(UpdateQueryP query, HCOUNTER* counter, const char* path, bool first_sample_on_init) {
235 assert(query != NULL, "invariant");
236 assert(counter != NULL, "invariant");
237 assert(path != NULL, "invariant");
238 if (query->query == NULL) {
239 if (open_query(query) != ERROR_SUCCESS) {
240 return OS_ERR;
241 }
242 }
243 assert(query->query != NULL, "invariant");
244 PDH_STATUS status = PdhDll::PdhAddCounter(query->query, path, 0, counter);
245 if (PDH_CSTATUS_NO_OBJECT == status || PDH_CSTATUS_NO_COUNTER == status) {
264
265 template <typename QueryP>
266 static OSReturn add_counter(QueryP counter_query, HCOUNTER* counter, const char* path, bool first_sample_on_init) {
267 assert(counter_query != NULL, "invariant");
268 assert(counter != NULL, "invariant");
269 assert(path != NULL, "invariant");
270 return add_counter(&counter_query->query, counter, path, first_sample_on_init);
271 }
272
273 static OSReturn add_counter(CounterQueryP counter_query, const char* path, bool first_sample_on_init) {
274 if (add_counter(counter_query, &counter_query->counter, path, first_sample_on_init) != OS_OK) {
275 // performance counter might be disabled in the registry
276 return OS_ERR;
277 }
278 counter_query->initialized = true;
279 return OS_OK;
280 }
281
282 static OSReturn add_process_counter(MultiCounterQueryP query, int slot_index, const char* path, bool first_sample_on_init) {
283 assert(query != NULL, "invariant");
284 assert(slot_index < query->noOfCounters, "invariant");
285 assert(query->counters[slot_index] == NULL, "invariant");
286 const OSReturn ret = add_counter(query, &query->counters[slot_index], path, first_sample_on_init);
287 if (OS_OK == ret) {
288 if (slot_index + 1 == query->noOfCounters) {
289 query->initialized = true;
290 }
291 }
292 return ret;
293 }
294
295 static int collect_query_data(UpdateQueryP update_query) {
296 assert(update_query != NULL, "invariant");
297 const s8 now = os::javaTimeMillis();
298 if (now - update_query->lastUpdate > min_update_interval_millis) {
299 if (PdhDll::PdhCollectQueryData(update_query->query) != ERROR_SUCCESS) {
300 return OS_ERR;
301 }
302 update_query->lastUpdate = now;
303 }
327 * The really bad part is that this list is reset as soon as a process exits:
328 * If \Process(java#1) exits, \Process(java#3) now becomes \Process(java#2) etc.
329 *
330 * The PDH api requires a process identifier to be submitted when registering
331 * a query, but as soon as the list resets, the query is invalidated (since the name changed).
332 *
333 * Solution:
334 * The #number identifier for a Process query can only decrease after process creation.
335 *
336 * We therefore create an array of counter queries for all process object instances
337 * up to and including ourselves:
338 *
339 * Ex. we come in as third process instance (java#2), we then create and register
340 * queries for the following Process object instances:
341 * java#0, java#1, java#2
342 *
343 * current_query_index_for_process() keeps track of the current "correct" query
344 * (in order to keep this index valid when the list resets from underneath,
345 * ensure to call current_query_index_for_process() before every query involving
346 * Process object instance data).
347 *
348 * if unable to query, returns OS_ERR(-1)
349 */
350 static int current_query_index_for_process() {
351 assert(process_image_name != NULL, "invariant");
352 assert(pdh_IDProcess_counter_fmt != NULL, "invariant");
353 HQUERY tmpQuery = NULL;
354 if (open_query(&tmpQuery) != ERROR_SUCCESS) {
355 return OS_ERR;
356 }
357 char counter[512];
358 HCOUNTER handle_counter = NULL;
359 // iterate over all instance indexes and try to find our own pid
360 for (int index = 0; index < max_intx; index++) {
361 jio_snprintf(counter, sizeof(counter) - 1, pdh_IDProcess_counter_fmt, index);
362 assert(strlen(counter) < sizeof(counter), "invariant");
363 if (PdhDll::PdhAddCounter(tmpQuery, counter, 0, &handle_counter) != ERROR_SUCCESS) {
364 pdh_cleanup(&tmpQuery, &handle_counter);
365 return OS_ERR;
366 }
367 const PDH_STATUS res = PdhDll::PdhCollectQueryData(tmpQuery);
368 if (res == PDH_INVALID_HANDLE || res == PDH_NO_DATA) {
369 pdh_cleanup(&tmpQuery, &handle_counter);
370 return OS_ERR;
371 } else {
372 PDH_FMT_COUNTERVALUE counter_value;
373 formatted_counter_value(handle_counter, PDH_FMT_LONG, &counter_value);
374 pdh_cleanup(NULL, &handle_counter);
375 if ((LONG)os::current_process_id() == counter_value.longValue) {
376 pdh_cleanup(&tmpQuery, NULL);
377 return index;
378 }
379 }
380 }
381 pdh_cleanup(&tmpQuery, NULL);
382 return OS_ERR;
383 }
384
385 static ProcessQueryP create_process_query() {
386 ProcessQueryP const process_query = NEW_C_HEAP_ARRAY(ProcessQueryS, 1, mtInternal);
387 memset(process_query, 0, sizeof(ProcessQueryS));
388 const int process_index = current_query_index_for_process();
389 const int current_process_idx = (OS_ERR == process_index) ? 0 : process_index;
390 process_query->set.queries = NEW_C_HEAP_ARRAY(MultiCounterQueryS, current_process_idx + 1, mtInternal);
391 memset(process_query->set.queries, 0, sizeof(MultiCounterQueryS) * (current_process_idx + 1));
392 process_query->process_index = current_process_idx;
393 process_query->set.size = current_process_idx + 1;
394 assert(process_query->set.size > process_query->process_index, "invariant");
395 return process_query;
396 }
397
398 static MultiCounterQueryP current_process_counter_query(ProcessQueryP process_query) {
399 assert(process_query != NULL, "invariant");
400 assert(process_query->process_index < process_query->set.size, "invariant");
401 return &process_query->set.queries[process_query->process_index];
402 }
403
404 static void clear_multi_counter(MultiCounterQueryP query) {
405 for (int i = 0; i < query->noOfCounters; ++i) {
406 pdh_cleanup(NULL, &query->counters[i]);
407 }
408 pdh_cleanup(&query->query.query, NULL);
409 query->initialized = false;
410 }
411
412 static int ensure_valid_process_query_index(ProcessQueryP process_query) {
413 assert(process_query != NULL, "invariant");
414 const int previous_process_idx = process_query->process_index;
415 if (previous_process_idx == 0) {
416 return previous_process_idx;
417 }
418 const int current_process_idx = current_query_index_for_process();
419 if (current_process_idx == previous_process_idx || OS_ERR == current_process_idx ||
420 current_process_idx >= process_query->set.size) {
421 return previous_process_idx;
422 }
423
424 assert(current_process_idx >= 0 && current_process_idx < process_query->set.size, "out of bounds!");
425 while (current_process_idx < process_query->set.size - 1) {
426 const int new_size = --process_query->set.size;
427 clear_multi_counter(&process_query->set.queries[new_size]);
428 }
429 assert(current_process_idx < process_query->set.size, "invariant");
430 process_query->process_index = current_process_idx;
431 return current_process_idx;
432 }
433
434 static MultiCounterQueryP current_process_query(ProcessQueryP process_query) {
435 assert(process_query != NULL, "invariant");
436 const int current_process_idx = ensure_valid_process_query_index(process_query);
437 assert(current_process_idx == process_query->process_index, "invariant");
438 assert(current_process_idx < process_query->set.size, "invariant");
439 return &process_query->set.queries[current_process_idx];
440 }
441
442 static int collect_process_query_data(ProcessQueryP process_query) {
443 assert(process_query != NULL, "invariant");
444 return collect_query_data(current_process_query(process_query));
445 }
446
447 static int query_process_counter(ProcessQueryP process_query, int slot_index, DWORD format, PDH_FMT_COUNTERVALUE* const value) {
448 MultiCounterQueryP const current_query = current_process_counter_query(process_query);
449 assert(current_query != NULL, "invariant");
450 assert(slot_index < current_query->noOfCounters, "invariant");
451 assert(current_query->counters[slot_index] != NULL, "invariant");
452 return formatted_counter_value(current_query->counters[slot_index], format, value);
453 }
454
455 /*
456 * Construct a fully qualified PDH path
457 *
458 * @param objectName a PDH Object string representation(required)
459 * @param counterName a PDH Counter string representation(required)
460 * @param imageName a process image name string, ex. "java" (opt)
461 * @param instance an instance string, ex. "0", "1", ... (opt)
462 * @return the fully qualified PDH path.
463 *
464 * Caller will need a ResourceMark.
465 *
466 * (PdhMakeCounterPath() seems buggy on concatenating instances, hence this function instead)
467 */
468 static const char* make_fully_qualified_counter_path(const char* object_name,
842 cpu_query->initialized = true;
843 // Query once to initialize the counters which require at least two samples
844 // (like the % CPU usage) to calculate correctly.
845 collect_query_data(cpu_query);
846 return OS_OK;
847 }
848
849 static int initialize_cpu_query(MultiCounterQueryP cpu_query, DWORD pdh_counter_idx) {
850 assert(cpu_query != NULL, "invariant");
851 assert(!cpu_query->initialized, "invariant");
852 const int logical_cpu_count = number_of_logical_cpus();
853 assert(logical_cpu_count >= os::processor_count(), "invariant");
854 // we also add another counter for instance "_Total"
855 if (allocate_counters(cpu_query, logical_cpu_count + 1) != OS_OK) {
856 return OS_ERR;
857 }
858 assert(cpu_query->noOfCounters == logical_cpu_count + 1, "invariant");
859 return initialize_cpu_query_counters(cpu_query, pdh_counter_idx);
860 }
861
862 static int initialize_process_counter(ProcessQueryP process_query, int slot_index, DWORD pdh_counter_index) {
863 char* localized_process_object;
864 if (lookup_name_by_index(PDH_PROCESS_IDX, &localized_process_object) != OS_OK) {
865 return OS_ERR;
866 }
867 assert(localized_process_object != NULL, "invariant");
868 char* localized_counter_name;
869 if (lookup_name_by_index(pdh_counter_index, &localized_counter_name) != OS_OK) {
870 return OS_ERR;
871 }
872 assert(localized_counter_name != NULL, "invariant");
873 for (int i = 0; i < process_query->set.size; ++i) {
874 char instanceIndexBuffer[32];
875 const char* counter_path = make_fully_qualified_counter_path(localized_process_object,
876 localized_counter_name,
877 process_image_name,
878 itoa(i, instanceIndexBuffer, 10));
879 if (counter_path == NULL) {
880 return OS_ERR;
881 }
882 MultiCounterQueryP const query = &process_query->set.queries[i];
883 if (add_process_counter(query, slot_index, counter_path, true)) {
884 return OS_ERR;
885 }
886 }
887 return OS_OK;
888 }
889
890 static CounterQueryP create_counter_query(DWORD pdh_object_idx, DWORD pdh_counter_idx) {
891 assert(is_valid_pdh_index(pdh_object_idx), "invariant");
892 assert(is_valid_pdh_index(pdh_counter_idx), "invariant");
893 CounterQueryP const query = create_counter_query();
894 const char* object = pdh_localized_artifact(pdh_object_idx);
895 assert(object != NULL, "invariant");
896 const char* counter = pdh_localized_artifact(pdh_counter_idx);
897 assert(counter != NULL, "invariant");
898 const char* full_counter_path = make_fully_qualified_counter_path(object, counter);
899 assert(full_counter_path != NULL, "invariant");
900 add_counter(query, full_counter_path, true);
901 return query;
902 }
949 pdh_initialized = true;
950 }
951 while (InterlockedCompareExchange(&critical_section, 0, 1) == 0);
952 return ret == OS_OK;
953 }
954
955 static void pdh_release() {
956 while (InterlockedCompareExchange(&critical_section, 1, 0) == 1);
957 const LONG prev_ref_count = InterlockedExchangeAdd(&reference_count, -1);
958 if (1 == prev_ref_count) {
959 deallocate();
960 pdh_initialized = false;
961 }
962 while (InterlockedCompareExchange(&critical_section, 0, 1) == 0);
963 }
964
965 class CPUPerformanceInterface::CPUPerformance : public CHeapObj<mtInternal> {
966 friend class CPUPerformanceInterface;
967 private:
968 CounterQueryP _context_switches;
969 ProcessQueryP _process_cpu_load;
970 MultiCounterQueryP _machine_cpu_load;
971
972 int cpu_load(int which_logical_cpu, double* cpu_load);
973 int context_switch_rate(double* rate);
974 int cpu_load_total_process(double* cpu_load);
975 int cpu_loads_process(double* jvm_user_load, double* jvm_kernel_load, double* psystemTotalLoad);
976 CPUPerformance();
977 ~CPUPerformance();
978 bool initialize();
979 };
980
981 class SystemProcessInterface::SystemProcesses : public CHeapObj<mtInternal> {
982 friend class SystemProcessInterface;
983 private:
984 class ProcessIterator : public CHeapObj<mtInternal> {
985 friend class SystemProcessInterface::SystemProcesses;
986 private:
987 HANDLE _hProcessSnap;
988 PROCESSENTRY32 _pe32;
989 BOOL _valid;
1001
1002 ProcessIterator* _iterator;
1003 SystemProcesses();
1004 ~SystemProcesses();
1005 bool initialize();
1006
1007 // information about system processes
1008 int system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const;
1009 };
1010
1011 CPUPerformanceInterface::CPUPerformance::CPUPerformance() : _context_switches(NULL), _process_cpu_load(NULL), _machine_cpu_load(NULL) {}
1012
1013 bool CPUPerformanceInterface::CPUPerformance::initialize() {
1014 if (!pdh_acquire()) {
1015 return false;
1016 }
1017 _context_switches = create_counter_query(PDH_SYSTEM_IDX, PDH_CONTEXT_SWITCH_RATE_IDX);
1018 if (_context_switches == NULL) {
1019 return false;
1020 }
1021 _process_cpu_load = create_process_query();
1022 if (_process_cpu_load == NULL) {
1023 return false;
1024 }
1025 if (allocate_counters(_process_cpu_load, 2) != OS_OK) {
1026 return false;
1027 }
1028 if (initialize_process_counter(_process_cpu_load, 0, PDH_PROCESSOR_TIME_IDX) != OS_OK) {
1029 return false;
1030 }
1031 if (initialize_process_counter(_process_cpu_load, 1, PDH_PRIV_PROCESSOR_TIME_IDX) != OS_OK) {
1032 return false;
1033 }
1034 _process_cpu_load->set.initialized = true;
1035
1036 _machine_cpu_load = create_multi_counter_query();
1037 if (_machine_cpu_load == NULL) {
1038 return false;
1039 }
1040 if (initialize_cpu_query(_machine_cpu_load, PDH_PROCESSOR_TIME_IDX) != OS_OK) {
1041 return false;
1042 }
1043 return true;
1044 }
1045
1046 CPUPerformanceInterface::CPUPerformance::~CPUPerformance() {
1047 if (_context_switches != NULL) {
1048 destroy_counter_query(_context_switches);
1049 _context_switches = NULL;
1050 }
1051 if (_process_cpu_load != NULL) {
1052 destroy_counter_query(_process_cpu_load);
1053 _process_cpu_load = NULL;
1054 }
1096 assert(_machine_cpu_load != NULL, "invariant");
1097 assert(which_logical_cpu < _machine_cpu_load->noOfCounters, "invariant");
1098 *cpu_load = .0;
1099 if (!_machine_cpu_load->initialized) {
1100 return OS_ERR;
1101 }
1102 if (collect_query_data(_machine_cpu_load)) {
1103 return OS_ERR;
1104 }
1105 // -1 is total (all cpus)
1106 const int counter_idx = -1 == which_logical_cpu ? _machine_cpu_load->noOfCounters - 1 : which_logical_cpu;
1107 PDH_FMT_COUNTERVALUE counter_value;
1108 formatted_counter_value(_machine_cpu_load->counters[counter_idx], PDH_FMT_DOUBLE, &counter_value);
1109 *cpu_load = counter_value.doubleValue / 100;
1110 return OS_OK;
1111 }
1112
1113 int CPUPerformanceInterface::CPUPerformance::cpu_load_total_process(double* cpu_load) {
1114 assert(_process_cpu_load != NULL, "invariant");
1115 *cpu_load = .0;
1116 if (!_process_cpu_load->set.initialized) {
1117 return OS_ERR;
1118 }
1119 if (collect_process_query_data(_process_cpu_load)) {
1120 return OS_ERR;
1121 }
1122 PDH_FMT_COUNTERVALUE counter_value;
1123 if (query_process_counter(_process_cpu_load, 0, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) {
1124 return OS_ERR;
1125 }
1126 double process_load = counter_value.doubleValue / cpu_factor();
1127 process_load = MIN2<double>(1, process_load);
1128 process_load = MAX2<double>(0, process_load);
1129 *cpu_load = process_load;
1130 return OS_OK;
1131 }
1132
1133 int CPUPerformanceInterface::CPUPerformance::cpu_loads_process(double* pjvmUserLoad,
1134 double* pjvmKernelLoad,
1135 double* psystemTotalLoad) {
1136 assert(pjvmUserLoad != NULL, "pjvmUserLoad is NULL!");
1137 assert(pjvmKernelLoad != NULL, "pjvmKernelLoad is NULL!");
1138 assert(psystemTotalLoad != NULL, "psystemTotalLoad is NULL!");
1139 *pjvmUserLoad = .0;
1140 *pjvmKernelLoad = .0;
1141 *psystemTotalLoad = .0;
1142 if (!_process_cpu_load->set.initialized) {
1143 return OS_ERR;
1144 }
1145 if (collect_process_query_data(_process_cpu_load)) {
1146 return OS_ERR;
1147 }
1148 double process_load = .0;
1149 PDH_FMT_COUNTERVALUE counter_value;
1150 // Read PDH_PROCESSOR_TIME_IDX
1151 if (query_process_counter(_process_cpu_load, 0, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) {
1152 return OS_ERR;
1153 }
1154 process_load = counter_value.doubleValue / cpu_factor();
1155 process_load = MIN2<double>(1, process_load);
1156 process_load = MAX2<double>(0, process_load);
1157 // Read PDH_PRIV_PROCESSOR_TIME_IDX
1158 if (query_process_counter(_process_cpu_load, 1, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) {
1159 return OS_ERR;
1160 }
1161 double kernel_load = counter_value.doubleValue / cpu_factor();
1162 kernel_load = MIN2<double>(1, kernel_load);
|