SoW-2020-0003: Trace Hit Counters
[lttng-tools.git] / src / bin / lttng / commands / view_map.c
1 /*
2 * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only
5 *
6 */
7
8 #include <math.h>
9
10 #include <common/dynamic-array.h>
11 #include <lttng/domain.h>
12 #include <lttng/lttng-error.h>
13 #include <lttng/map/map.h>
14 #include <lttng/map/map-internal.h>
15 #include <lttng/map/map-query.h>
16
17 #include "../command.h"
18
19 #include "common/argpar/argpar.h"
20
21 enum {
22 OPT_HELP,
23 OPT_SESSION,
24 OPT_LIST_OPTIONS,
25 OPT_USERSPACE,
26 OPT_KERNEL,
27 OPT_KEY,
28 };
29
30 static const
31 struct argpar_opt_descr view_map_options[] = {
32 { OPT_HELP, 'h', "help", false },
33 { OPT_SESSION, 's', "session", true },
34 { OPT_LIST_OPTIONS, '\0', "list-options", false },
35 /* Domains */
36 { OPT_USERSPACE, 'u', "userspace", false },
37 { OPT_KERNEL, 'k', "kernel", false },
38 { OPT_KEY, '\0', "key", true },
39 ARGPAR_OPT_DESCR_SENTINEL,
40 };
41
42 static
43 bool assign_domain_type(enum lttng_domain_type *dest,
44 enum lttng_domain_type src)
45 {
46 bool ret;
47
48 if (*dest == LTTNG_DOMAIN_NONE || *dest == src) {
49 *dest = src;
50 ret = true;
51 } else {
52 ERR("Multiple domains specified.");
53 ret = false;
54 }
55
56 return ret;
57 }
58
59 static
60 bool assign_string(char **dest, const char *src, const char *opt_name)
61 {
62 bool ret;
63
64 if (*dest) {
65 ERR("Duplicate %s given.", opt_name);
66 goto error;
67 }
68
69 *dest = strdup(src);
70 if (!*dest) {
71 ERR("Failed to allocate %s string.", opt_name);
72 goto error;
73 }
74
75 ret = true;
76 goto end;
77
78 error:
79 ret = false;
80
81 end:
82 return ret;
83 }
84
85 static
86 int compare_key_value_by_key(const void *a, const void *b)
87 {
88 const struct lttng_map_key_value_pair *kv_a =
89 *((const struct lttng_map_key_value_pair **) a);
90 const struct lttng_map_key_value_pair *kv_b =
91 *((const struct lttng_map_key_value_pair **) b);
92 const char *key_a, *key_b;
93 enum lttng_map_status map_status;
94
95 map_status = lttng_map_key_value_pair_get_key(kv_a, &key_a);
96 assert(map_status == LTTNG_MAP_STATUS_OK);
97
98 map_status = lttng_map_key_value_pair_get_key(kv_b, &key_b);
99 assert(map_status == LTTNG_MAP_STATUS_OK);
100
101 return strcmp(key_a, key_b);
102 }
103
104 static
105 void print_one_map_key_value_pair(const struct lttng_map_key_value_pair *kv_pair,
106 size_t key_len, size_t val_len)
107 {
108 const char *key = NULL;
109 int64_t value;
110 enum lttng_map_status status;
111 bool has_overflowed, has_underflowed;
112
113 status = lttng_map_key_value_pair_get_key(kv_pair, &key);
114 if (status != LTTNG_MAP_STATUS_OK) {
115 ERR("Failed to get key-value pair's key.");
116 goto end;
117 }
118
119 status = lttng_map_key_value_pair_get_value(kv_pair, &value);
120 if (status != LTTNG_MAP_STATUS_OK) {
121 ERR("Failed to get value-value pair's value.");
122 goto end;
123 }
124
125 has_overflowed = lttng_map_key_value_pair_get_has_overflowed(kv_pair);
126 has_underflowed = lttng_map_key_value_pair_get_has_underflowed(kv_pair);
127
128 /* Ensure the padding is nice using the `%*s` delimiter. */
129 MSG("| %*s | %*"PRId64" | %d | %d |", (int) -key_len, key,
130 (int) val_len, value, has_underflowed, has_overflowed);
131
132 end:
133 return;
134 }
135
136 static
137 void print_line(size_t key_len, size_t val_len)
138 {
139 int i;
140
141 _MSG("+");
142 for (i = 0; i < (int) key_len + 2; i++) {
143 _MSG("-");
144 }
145 _MSG("+");
146 for (i = 0; i < (int) val_len + 2; i++) {
147 _MSG("-");
148 }
149 MSG("+----+----+");
150 }
151
152 static
153 size_t number_of_digit(int64_t val)
154 {
155 size_t ret = 0;
156
157 if (val == 0) {
158 ret = 1;
159 goto end;
160 }
161
162 if (val < 0) {
163 /* Account for the minus sign. */
164 ret++;
165 val = llabs(val);
166 }
167
168 /*
169 * SOURCE:
170 * https://stackoverflow.com/questions/1068849/how-do-i-determine-the-number-of-digits-of-an-integer-in-c
171 * If the log10() call becomes too expensive, we could use a
172 * recursive approach to count the digits.
173 */
174 ret += floor(log10(val)) + 1;
175
176 end:
177 return ret;
178 }
179
180 static
181 void print_map_section_identifier(const struct lttng_map_key_value_pair_list *kv_pair_list)
182 {
183 switch (lttng_map_key_value_pair_list_get_type(kv_pair_list)) {
184 case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL:
185 _MSG("Kernel global map");
186 break;
187 case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED:
188 _MSG("Per-PID dead app aggregated map");
189 break;
190 case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID:
191 _MSG("PID: %"PRIu64, lttng_map_key_value_pair_list_get_identifer(
192 kv_pair_list));
193 break;
194 case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID:
195 _MSG("UID: %"PRIu64, lttng_map_key_value_pair_list_get_identifer(
196 kv_pair_list));
197 break;
198 default:
199 break;
200 }
201 _MSG(", CPU: ");
202 if (lttng_map_key_value_pair_list_get_summed_all_cpu(kv_pair_list)) {
203 MSG("ALL");
204 } else {
205 MSG("%"PRIu64, lttng_map_key_value_pair_list_get_cpu(kv_pair_list));
206 }
207 }
208
209 static
210 void print_map_table_header(size_t max_key_len, size_t max_val_len)
211 {
212 print_line(max_key_len, max_val_len);
213 /* Ensure the padding is nice using the `%*s` delimiter. */
214 MSG("| %*s | %*s | %s | %s |", (int) -max_key_len, "key",
215 (int) max_val_len, "val", "uf", "of");
216 }
217
218 static
219 enum lttng_error_code print_one_map_section(
220 const struct lttng_map_key_value_pair_list *kv_pair_list,
221 enum lttng_buffer_type buffer_type)
222 {
223 enum lttng_error_code ret;
224 enum lttng_map_status map_status;
225 size_t longest_key_len = strlen("key"), longest_val_len = strlen("val");
226 unsigned int i, key_value_pair_count;
227 struct lttng_dynamic_pointer_array sorted_kv_pair_list;
228
229 lttng_dynamic_pointer_array_init(&sorted_kv_pair_list, NULL);
230
231 map_status = lttng_map_key_value_pair_list_get_count(kv_pair_list,
232 &key_value_pair_count);
233 if (map_status != LTTNG_MAP_STATUS_OK) {
234 ERR("Failed to get key-value pair count.");
235 goto error;
236 }
237
238 for (i = 0; i < key_value_pair_count; i++) {
239 const char *cur_key;
240 int64_t cur_val;
241 const struct lttng_map_key_value_pair *pair =
242 lttng_map_key_value_pair_list_get_at_index(
243 kv_pair_list, i);
244
245 /* Add all key value pairs to the sorting array. */
246 lttng_dynamic_pointer_array_add_pointer(&sorted_kv_pair_list,
247 (void *) pair);
248
249 /* Keep track of the longest key. */
250 lttng_map_key_value_pair_get_key(pair, &cur_key);
251 longest_key_len = max(longest_key_len, strlen(cur_key));
252
253 /* Keep track of the longest value. */
254 lttng_map_key_value_pair_get_value(pair, &cur_val);
255 longest_val_len = max(longest_val_len, number_of_digit(cur_val));
256 }
257
258 qsort(sorted_kv_pair_list.array.buffer.data,
259 lttng_dynamic_pointer_array_get_count(&sorted_kv_pair_list),
260 sizeof(struct lttng_map_key_value_pair *),
261 compare_key_value_by_key);
262
263 print_map_section_identifier(kv_pair_list);
264 if (key_value_pair_count == 0) {
265 MSG(" No value in the map");
266 ret = LTTNG_OK;
267 goto end;
268 } else {
269 print_map_table_header(longest_key_len, longest_val_len);
270
271 for (i = 0; i < key_value_pair_count; i++) {
272 print_line(longest_key_len, longest_val_len);
273
274 print_one_map_key_value_pair(
275 lttng_dynamic_pointer_array_get_pointer(
276 &sorted_kv_pair_list, i),
277 longest_key_len, longest_val_len);
278 }
279
280 print_line(longest_key_len, longest_val_len);
281 }
282
283 ret = LTTNG_OK;
284 goto end;
285
286 error:
287 ret = LTTNG_ERR_MAP_VALUES_LIST_FAIL;
288 end:
289 lttng_dynamic_pointer_array_reset(&sorted_kv_pair_list);
290
291 return ret;
292 }
293
294 static
295 enum lttng_error_code print_one_map(struct lttng_handle *handle,
296 const struct lttng_map *map, const char *key_filter)
297 {
298 enum lttng_error_code ret;
299 enum lttng_map_status map_status;
300 struct lttng_map_content *map_content = NULL;
301 unsigned int i, map_content_section_count;
302 enum lttng_buffer_type buffer_type;
303 struct lttng_map_query *map_query = NULL;
304 enum lttng_map_query_config_buffer query_buffer_config;
305 enum lttng_map_query_config_app_bitness query_bitness_config;
306 enum lttng_map_query_status query_status;
307 const char *map_name = NULL;
308 enum lttng_map_bitness map_bitness;
309
310 map_status = lttng_map_get_name(map, &map_name);
311 assert(map_status == LTTNG_MAP_STATUS_OK);
312
313 map_bitness = lttng_map_get_bitness(map);
314
315 switch (lttng_map_get_buffer_type(map)) {
316 case LTTNG_BUFFER_GLOBAL:
317 query_buffer_config = LTTNG_MAP_QUERY_CONFIG_BUFFER_KERNEL_GLOBAL;
318 query_bitness_config = LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_KERNEL;
319 break;
320 case LTTNG_BUFFER_PER_UID:
321 query_buffer_config = LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL;
322 query_bitness_config = LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL;
323 break;
324 case LTTNG_BUFFER_PER_PID:
325 query_buffer_config = LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_ALL;
326 query_bitness_config = LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL;
327 break;
328 default:
329 ret = LTTNG_ERR_INVALID;
330 goto end;
331 }
332
333 map_query = lttng_map_query_create(LTTNG_MAP_QUERY_CONFIG_CPU_ALL,
334 query_buffer_config, query_bitness_config);
335 if (!map_query) {
336 ret = LTTNG_ERR_NOMEM;
337 ERR("Creating map query");
338 goto end;
339 }
340
341 if (key_filter) {
342 query_status = lttng_map_query_add_key_filter(map_query, key_filter);
343 assert(query_status == LTTNG_MAP_QUERY_STATUS_OK);
344 }
345
346 query_status = lttng_map_query_set_sum_by_cpu(map_query, true);
347 assert(query_status == LTTNG_MAP_QUERY_STATUS_OK);
348
349 /*
350 * We don't want to aggregate all uid (or pid) together for the lttng
351 * view-map command.
352 */
353 if (query_buffer_config == LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL) {
354 //query_status = lttng_map_query_set_sum_by_uid(map_query, false);
355 //assert(query_status == LTTNG_MAP_QUERY_STATUS_OK);
356 } else if (query_buffer_config == LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_ALL) {
357 query_status = lttng_map_query_set_sum_by_pid(map_query, false);
358 assert(query_status == LTTNG_MAP_QUERY_STATUS_OK);
359 }
360
361 /*
362 * If we have 32bits and 64bits maps, we want to aggregate both maps in
363 * a single table in the lttng view-map command.
364 */
365 if (query_bitness_config == LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL) {
366 //query_status = lttng_map_query_set_sum_by_app_bitness(map_query, true);
367 //assert(query_status == LTTNG_MAP_QUERY_STATUS_OK);
368 }
369
370 /* Fetch the key value pair_list from the sessiond */
371 ret = lttng_list_map_content(handle, map, map_query, &map_content);
372 if (ret != LTTNG_OK) {
373 ERR("Error listing map key value pair_list: %s.",
374 lttng_strerror(-ret));
375 goto end;
376 }
377
378 map_status = lttng_map_content_get_count(map_content,
379 &map_content_section_count);
380 if (map_status != LTTNG_MAP_STATUS_OK) {
381 ret = LTTNG_ERR_MAP_VALUES_LIST_FAIL;
382 ERR("Failed to get map content section count.");
383 goto end;
384 }
385
386 if (map_content_section_count == 0) {
387 DBG("Map %s is not found", map_name);
388 goto end;
389 }
390
391 MSG("Session: '%s', map: '%s', map bitness: %d", handle->session_name,
392 map_name, map_bitness);
393
394 buffer_type = lttng_map_content_get_buffer_type(map_content);
395
396 for (i = 0; i < map_content_section_count; i++) {
397 const struct lttng_map_key_value_pair_list *kv_pair_list =
398 lttng_map_content_get_at_index(map_content, i);
399
400 assert(kv_pair_list);
401 ret = print_one_map_section(kv_pair_list, buffer_type);
402 if (ret != LTTNG_OK) {
403 ERR("Error printing map section");
404 goto end;
405 }
406 }
407
408
409 ret = LTTNG_OK;
410 goto end;
411 end:
412 lttng_map_content_destroy(map_content);
413 return ret;
414 }
415
416 static
417 int view_map(struct lttng_handle *handle, const char *desired_map_name,
418 const char *key_filter)
419 {
420 enum lttng_error_code ret;
421 struct lttng_map_list *map_list = NULL;
422 enum lttng_map_status map_status;
423 bool desired_map_found = false;
424 unsigned int i, map_count;
425
426 DBG("Listing map(s) (%s)", desired_map_name ? : "<all>");
427 /*
428 * Query the sessiond for a list of all the maps that match the
429 * provided map name and domain (if any).
430 */
431 ret = lttng_list_maps(handle, &map_list);
432 if (ret != LTTNG_OK) {
433 ERR("Error getting map list");
434 goto end;
435 }
436
437 map_status = lttng_map_list_get_count(map_list, &map_count);
438 if (map_status != LTTNG_MAP_STATUS_OK) {
439 ERR("Error getting map list element count");
440 ret = -1;
441 goto end;
442 }
443
444 for (i = 0; i < map_count; i++) {
445 const struct lttng_map *map = NULL;
446 const char *map_name = NULL;
447 map = lttng_map_list_get_at_index(map_list, i);
448 if (!map) {
449 ERR("Error getting map from list: index = %u", i);
450 goto end;
451 }
452
453 map_status = lttng_map_get_name(map, &map_name);
454 if (map_status != LTTNG_MAP_STATUS_OK) {
455 ERR("Error getting map name");
456 ret = -1;
457 goto end;
458 }
459
460
461 if (desired_map_name != NULL) {
462 if (strncmp(map_name, desired_map_name, NAME_MAX) == 0) {
463 desired_map_found = true;
464 ret = print_one_map(handle, map, key_filter);
465 if (ret != LTTNG_OK) {
466 ret = -1;
467 goto end;
468 }
469 }
470 }
471 }
472
473 if (desired_map_name && !desired_map_found) {
474 DBG("Map %s in domain: %s (session %s)", desired_map_name,
475 lttng_strerror(-ret), handle->session_name);
476 ret = LTTNG_ERR_MAP_NOT_FOUND;
477 goto end;
478 }
479
480 end:
481 lttng_map_list_destroy(map_list);
482 return ret;
483 }
484
485 int cmd_view_map(int argc, const char **argv)
486 {
487 struct argpar_parse_ret argpar_parse_ret = { 0 };
488 enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE;
489 const char *opt_map_name = NULL;;
490 char *opt_session_name = NULL, *session_name = NULL;
491 char *opt_key_filter = NULL;
492 struct lttng_domain domain;
493 struct lttng_domain *domains = NULL;
494 struct lttng_handle *handle;
495
496 int ret, i;
497
498 memset(&domain, 0, sizeof(domain));
499
500 argpar_parse_ret = argpar_parse(argc - 1, argv + 1,
501 view_map_options, true);
502 if (!argpar_parse_ret.items) {
503 ERR("%s", argpar_parse_ret.error);
504 goto error;
505 }
506
507 for (i = 0; i < argpar_parse_ret.items->n_items; i++) {
508 struct argpar_item *item = argpar_parse_ret.items->items[i];
509
510 if (item->type == ARGPAR_ITEM_TYPE_OPT) {
511 struct argpar_item_opt *item_opt =
512 (struct argpar_item_opt *) item;
513
514 switch (item_opt->descr->id) {
515 case OPT_HELP:
516 SHOW_HELP();
517 ret = CMD_SUCCESS;
518 goto end;
519 case OPT_SESSION:
520 if (!assign_string(&opt_session_name, item_opt->arg,
521 "-s/--session")) {
522 goto error;
523 }
524 break;
525 case OPT_LIST_OPTIONS:
526 list_cmd_options_argpar(stdout,
527 view_map_options);
528 ret = CMD_SUCCESS;
529 goto end;
530 case OPT_USERSPACE:
531 if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_UST)) {
532 goto error;
533 }
534 break;
535
536 case OPT_KERNEL:
537 if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_KERNEL)) {
538 goto error;
539 }
540 break;
541 case OPT_KEY:
542 if (!assign_string(&opt_key_filter, item_opt->arg,
543 "--key")) {
544 goto error;
545 }
546 break;
547
548 default:
549 abort();
550 }
551
552 } else {
553 struct argpar_item_non_opt *item_non_opt =
554 (struct argpar_item_non_opt *) item;
555
556 if (opt_map_name) {
557 ERR("Unexpected argument: %s", item_non_opt->arg);
558 goto error;
559 }
560 opt_map_name = item_non_opt->arg;
561 }
562 }
563
564 if (!opt_map_name) {
565 ERR("Map name must be provided");
566 goto error;
567 }
568
569 if (!opt_session_name) {
570 DBG("No session name provided, print maps of the default session");
571 session_name = get_session_name();
572 if (session_name == NULL) {
573 goto error;
574 }
575 } else {
576 session_name = opt_session_name;
577 }
578
579 domain.type = domain_type;
580 handle = lttng_create_handle(session_name, &domain);
581 if (handle == NULL) {
582 ret = CMD_FATAL;
583 goto end;
584 }
585
586 if (domain.type != LTTNG_DOMAIN_NONE) {
587 /* Print maps of the given domain. */
588 ret = view_map(handle, opt_map_name, opt_key_filter);
589 if (ret != LTTNG_OK) {
590 goto error;
591 }
592 } else {
593 int domain_idx, nb_domain;
594 bool found_one_map = false;
595
596 /* We want all domain(s) */
597 nb_domain = lttng_list_domains(session_name, &domains);
598 if (nb_domain < 0) {
599 ret = CMD_ERROR;
600 ERR("%s", lttng_strerror(nb_domain));
601 goto end;
602 }
603
604 for (domain_idx = 0; domain_idx < nb_domain; domain_idx++) {
605 /* Clean handle before creating a new one */
606 if (handle) {
607 lttng_destroy_handle(handle);
608 }
609
610 handle = lttng_create_handle(session_name, &domains[domain_idx]);
611 if (handle == NULL) {
612 ret = CMD_FATAL;
613 goto end;
614 }
615
616 ret = view_map(handle, opt_map_name, opt_key_filter);
617
618 if (ret == LTTNG_OK) {
619 found_one_map = true;
620 } else if (ret == LTTNG_ERR_MAP_NOT_FOUND) {
621 DBG("Map not found in the current domain");
622 continue;
623 } else {
624 goto error;
625 }
626 }
627
628 if (!found_one_map) {
629 ERR("Map %s not found on any of the domains", opt_map_name);
630 goto error;
631
632 }
633 }
634
635 ret = CMD_SUCCESS;
636 goto end;
637 error:
638 ret = CMD_ERROR;
639
640 end:
641 if (!opt_session_name && session_name) {
642 free(session_name);
643 }
644
645 if (opt_key_filter) {
646 free(opt_key_filter);
647 }
648
649 argpar_parse_ret_fini(&argpar_parse_ret);
650 return ret;
651 }
This page took 0.045383 seconds and 5 git commands to generate.