2 * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
4 * SPDX-License-Identifier: LGPL-2.1-only
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>
17 #include "../command.h"
19 #include "common/argpar/argpar.h"
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 },
36 { OPT_USERSPACE
, 'u', "userspace", false },
37 { OPT_KERNEL
, 'k', "kernel", false },
38 { OPT_KEY
, '\0', "key", true },
39 ARGPAR_OPT_DESCR_SENTINEL
,
43 bool assign_domain_type(enum lttng_domain_type
*dest
,
44 enum lttng_domain_type src
)
48 if (*dest
== LTTNG_DOMAIN_NONE
|| *dest
== src
) {
52 ERR("Multiple domains specified.");
60 bool assign_string(char **dest
, const char *src
, const char *opt_name
)
65 ERR("Duplicate %s given.", opt_name
);
71 ERR("Failed to allocate %s string.", opt_name
);
86 int compare_key_value_by_key(const void *a
, const void *b
)
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
;
95 map_status
= lttng_map_key_value_pair_get_key(kv_a
, &key_a
);
96 assert(map_status
== LTTNG_MAP_STATUS_OK
);
98 map_status
= lttng_map_key_value_pair_get_key(kv_b
, &key_b
);
99 assert(map_status
== LTTNG_MAP_STATUS_OK
);
101 return strcmp(key_a
, key_b
);
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
)
108 const char *key
= NULL
;
110 enum lttng_map_status status
;
111 bool has_overflowed
, has_underflowed
;
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.");
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.");
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
);
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
);
137 void print_line(size_t key_len
, size_t val_len
)
142 for (i
= 0; i
< (int) key_len
+ 2; i
++) {
146 for (i
= 0; i
< (int) val_len
+ 2; i
++) {
153 size_t number_of_digit(int64_t val
)
163 /* Account for the minus sign. */
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.
174 ret
+= floor(log10(val
)) + 1;
181 void print_map_section_identifier(const struct lttng_map_key_value_pair_list
*kv_pair_list
)
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");
187 case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED
:
188 _MSG("Per-PID dead app aggregated map");
190 case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID
:
191 _MSG("PID: %"PRIu64
, lttng_map_key_value_pair_list_get_identifer(
194 case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID
:
195 _MSG("UID: %"PRIu64
, lttng_map_key_value_pair_list_get_identifer(
202 if (lttng_map_key_value_pair_list_get_summed_all_cpu(kv_pair_list
)) {
205 MSG("%"PRIu64
, lttng_map_key_value_pair_list_get_cpu(kv_pair_list
));
210 void print_map_table_header(size_t max_key_len
, size_t max_val_len
)
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");
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
)
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
;
229 lttng_dynamic_pointer_array_init(&sorted_kv_pair_list
, NULL
);
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.");
238 for (i
= 0; i
< key_value_pair_count
; i
++) {
241 const struct lttng_map_key_value_pair
*pair
=
242 lttng_map_key_value_pair_list_get_at_index(
245 /* Add all key value pairs to the sorting array. */
246 lttng_dynamic_pointer_array_add_pointer(&sorted_kv_pair_list
,
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
));
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
));
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
);
263 print_map_section_identifier(kv_pair_list
);
264 if (key_value_pair_count
== 0) {
265 MSG(" No value in the map");
269 print_map_table_header(longest_key_len
, longest_val_len
);
271 for (i
= 0; i
< key_value_pair_count
; i
++) {
272 print_line(longest_key_len
, longest_val_len
);
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
);
280 print_line(longest_key_len
, longest_val_len
);
287 ret
= LTTNG_ERR_MAP_VALUES_LIST_FAIL
;
289 lttng_dynamic_pointer_array_reset(&sorted_kv_pair_list
);
295 enum lttng_error_code
print_one_map(struct lttng_handle
*handle
,
296 const struct lttng_map
*map
, const char *key_filter
)
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
;
310 map_status
= lttng_map_get_name(map
, &map_name
);
311 assert(map_status
== LTTNG_MAP_STATUS_OK
);
313 map_bitness
= lttng_map_get_bitness(map
);
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
;
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
;
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
;
329 ret
= LTTNG_ERR_INVALID
;
333 map_query
= lttng_map_query_create(LTTNG_MAP_QUERY_CONFIG_CPU_ALL
,
334 query_buffer_config
, query_bitness_config
);
336 ret
= LTTNG_ERR_NOMEM
;
337 ERR("Creating map query");
342 query_status
= lttng_map_query_add_key_filter(map_query
, key_filter
);
343 assert(query_status
== LTTNG_MAP_QUERY_STATUS_OK
);
346 query_status
= lttng_map_query_set_sum_by_cpu(map_query
, true);
347 assert(query_status
== LTTNG_MAP_QUERY_STATUS_OK
);
350 * We don't want to aggregate all uid (or pid) together for the lttng
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
);
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.
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);
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
));
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.");
386 if (map_content_section_count
== 0) {
387 DBG("Map %s is not found", map_name
);
391 MSG("Session: '%s', map: '%s', map bitness: %d", handle
->session_name
,
392 map_name
, map_bitness
);
394 buffer_type
= lttng_map_content_get_buffer_type(map_content
);
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
);
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");
412 lttng_map_content_destroy(map_content
);
417 int view_map(struct lttng_handle
*handle
, const char *desired_map_name
,
418 const char *key_filter
)
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
;
426 DBG("Listing map(s) (%s)", desired_map_name
? : "<all>");
428 * Query the sessiond for a list of all the maps that match the
429 * provided map name and domain (if any).
431 ret
= lttng_list_maps(handle
, &map_list
);
432 if (ret
!= LTTNG_OK
) {
433 ERR("Error getting map list");
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");
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
);
449 ERR("Error getting map from list: index = %u", i
);
453 map_status
= lttng_map_get_name(map
, &map_name
);
454 if (map_status
!= LTTNG_MAP_STATUS_OK
) {
455 ERR("Error getting map name");
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
) {
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
;
481 lttng_map_list_destroy(map_list
);
485 int cmd_view_map(int argc
, const char **argv
)
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
;
498 memset(&domain
, 0, sizeof(domain
));
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
);
507 for (i
= 0; i
< argpar_parse_ret
.items
->n_items
; i
++) {
508 struct argpar_item
*item
= argpar_parse_ret
.items
->items
[i
];
510 if (item
->type
== ARGPAR_ITEM_TYPE_OPT
) {
511 struct argpar_item_opt
*item_opt
=
512 (struct argpar_item_opt
*) item
;
514 switch (item_opt
->descr
->id
) {
520 if (!assign_string(&opt_session_name
, item_opt
->arg
,
525 case OPT_LIST_OPTIONS
:
526 list_cmd_options_argpar(stdout
,
531 if (!assign_domain_type(&domain_type
, LTTNG_DOMAIN_UST
)) {
537 if (!assign_domain_type(&domain_type
, LTTNG_DOMAIN_KERNEL
)) {
542 if (!assign_string(&opt_key_filter
, item_opt
->arg
,
553 struct argpar_item_non_opt
*item_non_opt
=
554 (struct argpar_item_non_opt
*) item
;
557 ERR("Unexpected argument: %s", item_non_opt
->arg
);
560 opt_map_name
= item_non_opt
->arg
;
565 ERR("Map name must be provided");
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
) {
576 session_name
= opt_session_name
;
579 domain
.type
= domain_type
;
580 handle
= lttng_create_handle(session_name
, &domain
);
581 if (handle
== NULL
) {
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
) {
594 bool found_one_map
= false;
596 /* We want all domain(s) */
597 nb_domain
= lttng_list_domains(session_name
, &domains
);
600 ERR("%s", lttng_strerror(nb_domain
));
604 for (i
= 0; i
< nb_domain
; i
++) {
605 /* Clean handle before creating a new one */
607 lttng_destroy_handle(handle
);
610 handle
= lttng_create_handle(session_name
, &domains
[i
]);
611 if (handle
== NULL
) {
616 ret
= view_map(handle
, opt_map_name
, opt_key_filter
);
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");
628 if (!found_one_map
) {
629 ERR("Map %s not found on any of the domains", opt_map_name
);
641 if (!opt_session_name
&& session_name
) {
645 if (opt_key_filter
) {
646 free(opt_key_filter
);
649 argpar_parse_ret_fini(&argpar_parse_ret
);