/* * zmemtopo - Show CEC memory topology data on System z * * Copyright IBM Corp. 2025 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include "lib/util_fmt.h" #include "lib/util_libc.h" #include "lib/util_list.h" #include "lib/util_opt.h" #include "lib/util_path.h" #include "lib/util_prg.h" #include "zmemtopo.h" #include "zmemtopo_cli.h" static const struct util_prg prg = { .desc = "Display CEC memory topology of allocated memory increments.", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2025, .pub_last = 2025, }, UTIL_PRG_COPYRIGHT_END } }; static struct zmemtopo_globals { unsigned int nesting_level; unsigned int max_level; unsigned int tree_full; unsigned int tree_reverse; unsigned int table_view; unsigned int sort_field; unsigned int ascii; unsigned int fmt_specified; char *partition_filter; enum util_fmt_t format; enum util_fmt_flags_t fmt_flags; } g; static void parse_nesting_level(char *arg) { unsigned long level; level = strtoul(arg, NULL, 10); if (level < NESTING_LVL_MIN || level > NESTING_LVL_MAX) errx(EXIT_FAILURE, "The nesting level given is not valid"); g.nesting_level = (unsigned int)level; } static void parse_sort_field(char *arg) { if (strcasecmp(arg, "nr") == 0) g.sort_field = SORT_NR; else if (strcasecmp(arg, "lpar") == 0) g.sort_field = SORT_NAME; else if (strcasecmp(arg, "size") == 0) g.sort_field = SORT_SIZE; else errx(EXIT_FAILURE, "%s is not a valid sort field option", arg); } static void parse_fmt_options(char *opt) { if (!util_fmt_name_to_type(opt, &g.format)) { errx(EXIT_FAILURE, "The format provided is not valid. Supported formats are: %s", FMT_TYPE_NAMES); } g.fmt_flags = FMT_HANDLEINT | FMT_KEEPINVAL; if (g.format == FMT_CSV) g.fmt_flags |= FMT_NOMETA | FMT_QUOTEALL; else g.fmt_flags |= FMT_DEFAULT; g.fmt_specified = 1; } static void parse_args(int argc, char *argv[]) { int opt; do { opt = util_opt_getopt_long(argc, argv); switch (opt) { case 'v': util_prg_print_version(); exit(EXIT_SUCCESS); case 'h': util_prg_print_help(); util_opt_print_help(); exit(EXIT_SUCCESS); case 'l': parse_nesting_level(optarg); break; case 't': g.table_view = 1; break; case 'f': g.tree_full = 1; break; case 'r': g.tree_reverse = 1; break; case 's': parse_sort_field(optarg); break; case 'p': g.partition_filter = optarg; break; case 'i': g.ascii = 1; break; case OPT_FORMAT: parse_fmt_options(optarg); break; case -1: break; default: util_opt_print_parse_error(opt, argv); exit(EXIT_FAILURE); } } while (opt != -1); if (optind != argc) { errx(EXIT_FAILURE, "An invalid parameter %s was entered", argv[optind]); } if (g.table_view && (g.tree_full || g.tree_reverse)) { errx(EXIT_FAILURE, "The --full and --reverse options cannot be used with the table view"); } if (g.fmt_specified && (g.tree_full || g.tree_reverse || g.table_view || g.ascii)) { errx(EXIT_FAILURE, "The --format FORMAT option cannot be combined with human readable output modifiers"); } } static void diag310_handle_error(int err) { switch (err) { case EACCES: errx(EXIT_FAILURE, "Check your permissions. You must have access to query memory topology"); case ENODATA: errx(EXIT_FAILURE, "Nesting level %u is not supported", g.nesting_level); case EINVAL: errx(EXIT_FAILURE, "Check the zmemtopo arguments, the parameters received are not valid"); case EOPNOTSUPP: errx(EXIT_FAILURE, "Memory topology querying is not supported"); case EBUSY: errx(EXIT_FAILURE, "Memory topology querying is busy"); default: warnx("An unknown error occurred"); } } static void diag310_check_support(void) { if (util_path_exists(DIAG_PATH)) return; errx(EXIT_FAILURE, "Memory topology querying is not supported"); } static int diag310_open_device(int flags) { int fd; fd = open(DIAG_PATH, flags); if (fd < 0) { diag310_handle_error(errno); errx(EXIT_FAILURE, "Could not open %s", DIAG_PATH); } return fd; } static unsigned long diag310_get_stride(void) { size_t stride; int fd; fd = diag310_open_device(O_RDONLY); if (ioctl(fd, DIAG310_GET_STRIDE, &stride)) { diag310_handle_error(errno); errx(EXIT_FAILURE, "An error occurred while reading stride from %s", DIAG_PATH); } close(fd); return stride; } static unsigned long diag310_get_memtop_length(void) { size_t data_len; int fd; fd = diag310_open_device(O_RDONLY); data_len = g.nesting_level; if (ioctl(fd, DIAG310_GET_MEMTOPLEN, &data_len)) { diag310_handle_error(errno); errx(EXIT_FAILURE, "An error occurred while reading buffer length from %s", DIAG_PATH); } close(fd); return data_len; } static void *diag310_get_memtop_data(void) { struct diag310_memtop data; unsigned long buffer_size; char *buf; int fd; buffer_size = diag310_get_memtop_length(); buf = util_zalloc(buffer_size * sizeof(*buf)); fd = diag310_open_device(O_RDONLY); data.nesting_lvl = g.nesting_level; data.address = (uint64_t)buf; if (ioctl(fd, DIAG310_GET_MEMTOPBUF, data)) { diag310_handle_error(errno); errx(EXIT_FAILURE, "An error occurred while reading topology data from %s", DIAG_PATH); } close(fd); return buf; } static struct stride_unit determine_stride_unit(void) { static const char * const suffix[] = {"b", "K", "M", "G", "T"}; unsigned long scale[] = {1, SCALE_KB, SCALE_MB, SCALE_GB, SCALE_TB}; struct stride_unit unit; unsigned long stride; unsigned int i; stride = diag310_get_stride(); stride *= SCALE_MB; unit.size = stride; for (i = 0; stride >= SCALE_KB; i++) stride /= SCALE_KB; snprintf(unit.suffix, UNIT_LEN, "%s", suffix[i]); unit.scale = scale[i]; return unit; } static iconv_t iconv_ebcdic_ascii; static void ebcdic_iconv_deinit(void) { if (iconv_close(iconv_ebcdic_ascii)) { errx(EXIT_FAILURE, "The zmemtopo command could not deinitialize iconv"); } } static void ebcdic_iconv_init(void) { iconv_ebcdic_ascii = iconv_open("ISO-8859-1", "EBCDIC-US"); if (iconv_ebcdic_ascii == (iconv_t)-1) { errx(EXIT_FAILURE, "The zmemtopo command could not initialize iconv"); } } static void ebcdic_to_ascii(char *in, char *out, size_t size) { size_t size_out, size_in, rc; size_out = size; size_in = size; rc = iconv(iconv_ebcdic_ascii, &in, &size_in, &out, &size_out); if (rc == (size_t)-1) errx(EXIT_FAILURE, "Code page translation EBCDIC-ASCII failed"); } static void topology_entries_add_entry(struct topology_entry *entry, unsigned short *ices, unsigned int len) { unsigned short *increments; unsigned int index, new_count; index = entry->count; new_count = index + len; increments = util_realloc(entry->increments, new_count * sizeof(*increments)); if (!ices) memset(increments + index, 0, len * sizeof(*increments)); else memcpy(increments + index, ices, len * sizeof(*ices)); entry->increments = increments; entry->count = new_count; } static void partition_set_name(struct partition *part, char *pname) { ebcdic_to_ascii(pname, part->part_name, LPAR_NAME_LEN); } static void partition_add_entry(struct partition *part, unsigned int *max_entry_nr, struct diag310_tle *tle) { unsigned int i; topology_entries_add_entry(&part->entries[tle->cl - 1], tle->ices, tle->ice_nr); if (tle->cl == g.nesting_level) { for (i = 0; i < tle->ice_nr; i++) part->increment_total += tle->ices[i]; } /* Fill missing entries with padding to correctly represent topology */ if (tle->ice_nr > 1 || tle->ices[0]) return; for (i = tle->cl - 1; i >= g.nesting_level; i--) { topology_entries_add_entry(&part->entries[i - 1], NULL, max_entry_nr[i - 1]); } } static struct partition *partition_create(struct diag310_p_hdr *p_hdr) { struct partition *part; part = util_zalloc(sizeof(*part)); partition_set_name(part, p_hdr->pname); part->increment_total = 0; part->part_nr = p_hdr->pn; return part; } static void partition_list_free(struct partitions *parts) { struct partition *cur, *next; unsigned int level; util_list_iterate_safe(parts->list, cur, next) { util_list_remove(parts->list, cur); for (level = g.nesting_level; level <= g.max_level; level++) free(cur->entries[level - 1].increments); free(cur); } util_list_free(parts->list); free(parts); } static struct partitions *partition_list_create(void) { struct partitions *ptr; ptr = util_zalloc(sizeof(*ptr)); ptr->list = util_list_new(struct partition, node); return ptr; } static void partition_list_calculate_level_lengths(struct partitions *parts, struct view_data *vdata) { struct partition *cur; unsigned int level; cur = util_list_start(parts->list); for (level = g.max_level; level >= g.nesting_level; level--) { vdata->level_len[level - 1] = cur->entries[level - 1].count; if (level < g.max_level) vdata->level_len[level - 1] /= vdata->level_len[level]; } } static uint64_t jump_over_padding(uint64_t addr) { size_t offset; offset = sizeof(uint64_t) * 2; if (addr % offset) addr = (addr / offset + 1) * offset; return addr; } static void partition_list_populate(void *data, struct partitions *parts) { unsigned int max_entry_nr[NESTING_LVL_MAX]; unsigned int entry, lpar_idx; struct diag310_p_hdr *p_hdr; struct diag310_t_hdr *t_hdr; struct diag310_tle *tle; unsigned long tle_bytes; struct partition *part; memset(max_entry_nr, 0, sizeof(max_entry_nr)); t_hdr = (struct diag310_t_hdr *)data; p_hdr = (void *)t_hdr + sizeof(*t_hdr); tle = (void *)p_hdr + sizeof(*p_hdr); /* Traverse over the data first to explore dimentions */ for (lpar_idx = 0; lpar_idx < t_hdr->lpar_cnt; lpar_idx++) { for (entry = 0; entry < p_hdr->tie; entry++) { if (g.max_level < tle->cl) g.max_level = tle->cl; if (max_entry_nr[tle->cl - 1] < tle->ice_nr) max_entry_nr[tle->cl - 1] = tle->ice_nr; tle_bytes = sizeof(*tle->ices) * (tle->ice_nr + 1); tle = (void *)tle + tle_bytes; } p_hdr = (void *)jump_over_padding((uint64_t)(void *)tle); tle = (void *)p_hdr + sizeof(*p_hdr); } t_hdr = (struct diag310_t_hdr *)data; p_hdr = (void *)t_hdr + sizeof(*t_hdr); tle = (void *)p_hdr + sizeof(*p_hdr); for (lpar_idx = 0; lpar_idx < t_hdr->lpar_cnt; lpar_idx++) { if (!p_hdr->tie) { p_hdr = (void *)jump_over_padding((uint64_t)(void *)tle); tle = (void *)p_hdr + sizeof(*p_hdr); continue; } part = partition_create(p_hdr); for (entry = 0; entry < p_hdr->tie; entry++) { partition_add_entry(part, max_entry_nr, tle); tle_bytes = sizeof(*tle->ices) * (tle->ice_nr + 1); tle = (void *)tle + tle_bytes; } p_hdr = (void *)jump_over_padding((uint64_t)(void *)tle); tle = (void *)p_hdr + sizeof(*p_hdr); util_list_add_tail(parts->list, part); } p_hdr = (void *)t_hdr + t_hdr->this_part; parts->this_part = p_hdr->pn; memcpy(parts->tod, t_hdr->tod, sizeof(t_hdr->tod)); } static int part_cmp_sum(void *a, void *b, void *UNUSED(data)) { struct partition *pa = a, *pb = b; if (pa->increment_total == pb->increment_total) return 0; return pa->increment_total > pb->increment_total ? 1 : -1; } static int part_cmp_lpar(void *a, void *b, void *UNUSED(data)) { struct partition *pa = a, *pb = b; return strcmp(pa->part_name, pb->part_name); } static int part_cmp_nr(void *a, void *b, void *UNUSED(data)) { struct partition *pa = a, *pb = b; if (pa->part_nr == pb->part_nr) return 0; return pa->part_nr > pb->part_nr ? 1 : -1; } static void partition_list_sort(struct partitions *parts) { switch (g.sort_field) { case SORT_NAME: util_list_sort(parts->list, part_cmp_lpar, NULL); break; case SORT_SIZE: util_list_sort(parts->list, part_cmp_sum, NULL); break; case SORT_NR: default: util_list_sort(parts->list, part_cmp_nr, NULL); break; } } static int partition_filter_matches(struct partition *part) { int result; if (!part) return 0; if (!g.partition_filter) return 1; if (!strlen(g.partition_filter)) return 1; result = 0; if (strcasestr(part->part_name, g.partition_filter)) result = 1; return result; } static unsigned int find_entry_cell_size(struct partitions *parts) { unsigned int max_digit, max_increment; struct partition *cur; max_increment = 0; max_digit = 1; util_list_iterate(parts->list, cur) { if (cur->increment_total > max_increment) max_increment = cur->increment_total; } while (max_increment) { max_increment /= 10; max_digit++; } return max_digit > ENTRY_DIGIT ? max_digit : ENTRY_DIGIT; } static void concat_w_padding(char **buf, unsigned int padding, unsigned int direction, const char *fmt, ...) { va_list args; char *cell; va_start(args, fmt); util_vasprintf(&cell, fmt, args); va_end(args); if (direction) util_concatf(buf, "%-*s", padding, cell); else util_concatf(buf, "%*s", padding, cell); free(cell); } static void table_print_level_separator(char **buf, unsigned int col, unsigned int *level_length) { unsigned int level, col_max; col_max = 1; for (level = g.max_level; level >= g.nesting_level; level--) col_max *= level_length[level - 1]; if (col == col_max) return; for (level = g.max_level; level > g.nesting_level; level--) { if (col % (col_max / level_length[level - 1]) == 0) util_concatf(buf, " "); } } static void table_print_row(char **buf, struct partition *cur, struct view_data *vdata) { struct topology_entry *entries; unsigned int i, s_padding; s_padding = vdata->entry_len >= SUM_PAD ? vdata->entry_len : SUM_PAD; concat_w_padding(buf, LPAR_NO_LEN, 1, "%2d", cur->part_nr); concat_w_padding(buf, LPAR_NAME_LEN, 1, "%s", cur->part_name); concat_w_padding(buf, s_padding, 0, "%lu", cur->increment_total); entries = &cur->entries[g.nesting_level - 1]; for (i = 0; i < entries->count; i++) { if (entries->increments[i]) { concat_w_padding(buf, vdata->entry_len, 0, "%lu", entries->increments[i]); } else { concat_w_padding(buf, vdata->entry_len, 0, "-"); } table_print_level_separator(buf, i + 1, vdata->level_len); } util_concatf(buf, "\n"); } static void table_print_header(char **buf, struct view_data *vdata) { unsigned int level, l_padding, s_padding, col_max, i, idx; unsigned int *level_len; col_max = 1; level_len = vdata->level_len; for (level = g.max_level; level >= g.nesting_level; level--) col_max *= level_len[level - 1]; s_padding = vdata->entry_len >= SUM_PAD ? vdata->entry_len : SUM_PAD; l_padding = s_padding + LPAR_NO_LEN + LPAR_NAME_LEN; for (level = g.max_level; level >= g.nesting_level; level--) { concat_w_padding(buf, l_padding, 0, "LEVEL %u", level); for (i = 0; i < col_max; i++) { if (level == g.max_level) idx = i / (col_max / level_len[level - 1]); else idx = i % (col_max / level_len[level]); concat_w_padding(buf, vdata->entry_len, 0, "%u", idx); table_print_level_separator(buf, i + 1, level_len); } util_concatf(buf, "\n"); } util_concatf(buf, "%-*s", LPAR_NO_LEN, "NR"); util_concatf(buf, "%-*s", LPAR_NAME_LEN, "LPAR"); util_concatf(buf, "%*s\n", s_padding, "SUM"); } static void table_print(struct partitions *parts) { struct stride_unit unit; struct view_data *vdata; struct partition *cur; char **table; unit = determine_stride_unit(); vdata = util_zalloc(sizeof(*vdata)); table = util_zalloc(sizeof(*table)); vdata->entry_len = find_entry_cell_size(parts); partition_list_calculate_level_lengths(parts, vdata); table_print_header(table, vdata); util_list_iterate(parts->list, cur) { if (!partition_filter_matches(cur)) continue; table_print_row(table, cur, vdata); } printf("%s\n", *table); printf("Increment size: %lu%s\n", unit.size / unit.scale, unit.suffix); free(vdata); free(*table); free(table); } static unsigned int tree_find_cell_len(void) { unsigned int indent; indent = g.max_level - g.nesting_level; if (g.tree_reverse) indent++; return indent + LEVEL_LEN; } static void tree_create_header(char **buf, struct view_data *vdata) { if (g.tree_reverse) util_concatf(buf, "%-*s", vdata->cell_len, "LEVEL/LPAR"); else util_concatf(buf, "%-*s", vdata->cell_len, "LPAR/LEVEL"); util_concatf(buf, "%*s\n", vdata->entry_len, "SIZE"); } static unsigned int tree_add_indent(char **buf, unsigned int level, unsigned int *end_flag) { unsigned int i, nesting; char *prefix; if (level > g.max_level) return 0; prefix = util_strdup(""); nesting = g.max_level - level; if (g.ascii) { for (i = 0; i < nesting; i++) { util_concatf(&prefix, "%s%s", end_flag[i] ? " " : ASCII_V, " "); } util_concatf(&prefix, "%s", end_flag[i] ? ASCII_UR : ASCII_VR); } else { for (i = 0; i < nesting; i++) { util_concatf(&prefix, "%s%s", end_flag[i] ? UTF_SP : UTF_V, UTF_SP); } util_concatf(&prefix, "%s", end_flag[i] ? UTF_UR : UTF_VR); } util_concatf(buf, "%s", prefix); free(prefix); if (g.ascii) return (nesting + 1) * 2; return nesting * 2 + 1; } static unsigned int entry_exists_at(struct topology_entry *entries, unsigned int start, unsigned int end) { unsigned int idx; if (g.tree_full) return start == end; for (idx = start; idx < end; idx++) { if (entries->increments[idx]) return 0; } return 1; } static void tree_part_to_level(char **buf, struct partition *part, unsigned int step, unsigned int level, struct view_data *vdata) { unsigned int start, end, idx, flag_idx, indent; struct topology_entry *entries; unsigned long memory_size; struct stride_unit unit; if (level < g.nesting_level) return; unit = vdata->unit; entries = &part->entries[level - 1]; start = step * vdata->level_len[level - 1]; end = (step + 1) * vdata->level_len[level - 1]; flag_idx = g.max_level - level; for (idx = start; idx < end; idx++) { memory_size = entries->increments[idx] * unit.size / unit.scale; if (!memory_size && !g.tree_full) continue; vdata->end_flag[flag_idx] = entry_exists_at(entries, idx + 1, end); indent = tree_add_indent(buf, level, vdata->end_flag); concat_w_padding(buf, vdata->cell_len - indent, 1, "LEVEL%u_%u", level, idx % vdata->level_len[level - 1]); if (memory_size) { concat_w_padding(buf, vdata->entry_len, 0, "%lu%s", memory_size, unit.suffix); } else { concat_w_padding(buf, vdata->entry_len, 0, "-"); } util_concatf(buf, "\n"); tree_part_to_level(buf, part, idx, level - 1, vdata); } } static void tree_create(char **tree, struct partitions *parts, struct view_data *vdata) { struct stride_unit unit; unsigned long part_size; struct partition *cur; unit = vdata->unit; tree_create_header(tree, vdata); util_list_iterate(parts->list, cur) { if (!partition_filter_matches(cur)) continue; util_concatf(tree, "%-*s", vdata->cell_len, cur->part_name); part_size = cur->increment_total * unit.size / unit.scale; concat_w_padding(tree, vdata->entry_len, 0, "%u%s", part_size, unit.suffix); util_concatf(tree, "\n"); tree_part_to_level(tree, cur, 0, g.max_level, vdata); } } static unsigned int is_increment_at(struct partitions *parts, struct partition *cur, unsigned int level, unsigned int idx) { struct partition *next; if (!cur) next = util_list_start(parts->list); else next = util_list_next(parts->list, cur); for (; next; next = util_list_next(parts->list, next)) { if (!next->entries[level - 1].increments[idx]) continue; if (partition_filter_matches(next)) return 1; } return 0; } static unsigned int rtree_level_total_size(struct partitions *parts, unsigned int level, unsigned int idx) { struct partition *cur; unsigned int total; total = 0; util_list_iterate(parts->list, cur) { if (!cur->entries[level - 1].increments[idx]) continue; total += cur->entries[level - 1].increments[idx]; } return total; } static void rtree_print_parts(char **buf, struct partitions *parts, unsigned int idx, unsigned int level, struct view_data *vdata) { struct topology_entry *entries; unsigned int flag_idx, indent; unsigned long incr_size; struct stride_unit unit; struct partition *cur; unit = vdata->unit; flag_idx = (g.max_level - level) + 1; util_list_iterate(parts->list, cur) { if (!partition_filter_matches(cur)) continue; entries = &cur->entries[level - 1]; if (!entries->increments[idx]) continue; vdata->end_flag[flag_idx] = !is_increment_at(parts, cur, level, idx); indent = tree_add_indent(buf, level - 1, vdata->end_flag); concat_w_padding(buf, vdata->cell_len - indent, 1, "%s", cur->part_name); incr_size = entries->increments[idx] * unit.size / unit.scale; concat_w_padding(buf, vdata->entry_len, 0, "%lu%s", incr_size, unit.suffix); util_concatf(buf, "\n"); if (vdata->end_flag[flag_idx]) break; } } static unsigned int rtree_is_last_entry(struct partitions *parts, unsigned int idx, unsigned int level, unsigned int end) { unsigned int tidx; if (g.partition_filter && !g.tree_full) { for (tidx = idx + 1; tidx < end; tidx++) { if (is_increment_at(parts, NULL, level, tidx)) return 0; } return 1; } return (idx + 1) == end; } static void rtree_level_to_part(char **buf, struct partitions *parts, unsigned int step, unsigned int level, struct view_data *vdata) { unsigned int start, end, idx, flag_idx, indent; unsigned long incr_size; struct stride_unit unit; if (level < g.nesting_level) return; unit = vdata->unit; flag_idx = g.max_level - level; start = step * vdata->level_len[level - 1]; end = (step + 1) * vdata->level_len[level - 1]; for (idx = start; idx < end; idx++) { if (!is_increment_at(parts, NULL, level, idx) && !g.tree_full) continue; indent = 0; vdata->end_flag[flag_idx] = rtree_is_last_entry(parts, idx, level, end); if (level != g.max_level) indent = tree_add_indent(buf, level, vdata->end_flag); concat_w_padding(buf, vdata->cell_len - indent, 1, "LEVEL%u_%u", level, idx % vdata->level_len[level - 1]); incr_size = rtree_level_total_size(parts, level, idx); incr_size = incr_size * unit.size / unit.scale; if (incr_size) { concat_w_padding(buf, vdata->entry_len, 0, "%u%s", incr_size, unit.suffix); } else { concat_w_padding(buf, vdata->entry_len, 0, "-"); } util_concatf(buf, "\n"); rtree_level_to_part(buf, parts, idx, level - 1, vdata); if (level != g.nesting_level) continue; rtree_print_parts(buf, parts, idx, level, vdata); } } static void rtree_create(char **tree, struct partitions *parts, struct view_data *vdata) { tree_create_header(tree, vdata); rtree_level_to_part(tree, parts, 0, g.max_level, vdata); } static void tree_print(struct partitions *parts) { struct view_data *vdata; char **tree; vdata = util_zalloc(sizeof(*vdata)); vdata->unit = determine_stride_unit(); vdata->entry_len = find_entry_cell_size(parts) + UNIT_LEN; vdata->cell_len = tree_find_cell_len(); partition_list_calculate_level_lengths(parts, vdata); tree = util_zalloc(sizeof(*tree)); if (g.tree_reverse) rtree_create(tree, parts, vdata); else tree_create(tree, parts, vdata); printf("%s\n", *tree); free(vdata); free(*tree); free(tree); } static char *parseable_create_tod_hex(char *tod) { size_t total_size; unsigned int i; char *ptr; int n; total_size = TOD_LEN * 2 + 2; ptr = util_zalloc(total_size * sizeof(*ptr)); n = snprintf(ptr, total_size, "0x"); for (i = 0; i < TOD_LEN; i++) n += snprintf(ptr + n, total_size, "%x", tod[i]); return ptr; } static void parseable_generic_level(struct partition *part, unsigned int step, unsigned int level, unsigned int *level_len) { struct topology_entry *entries; unsigned int i, start, end; if (level < g.nesting_level) return; entries = &part->entries[level - 1]; start = step * level_len[level - 1]; end = (step + 1) * level_len[level - 1]; util_fmt_obj_start(FMT_LIST, "topology"); for (i = start; i < end; i++) { util_fmt_obj_start(FMT_ROW, NULL); util_fmt_pair(FMT_DEFAULT, "level", "%u", level); util_fmt_pair(FMT_DEFAULT, "entry_idx", "%u", i % level_len[level - 1]); util_fmt_pair(FMT_DEFAULT, "increment_count", "%u", entries->increments[i]); parseable_generic_level(part, i, level - 1, level_len); util_fmt_obj_end(); } util_fmt_obj_end(); } static void parseable_generic(struct partitions *parts, struct view_data *vdata) { struct stride_unit unit; struct partition *cur; char *tod; unit = vdata->unit; tod = parseable_create_tod_hex(parts->tod); util_fmt_obj_start(FMT_DEFAULT, "zmemtopo"); util_fmt_pair(FMT_QUOTE | FMT_PERSIST, "report_tod", "%s", tod); util_fmt_pair(FMT_PERSIST, "report_partition_nr", "%u", parts->this_part); util_fmt_pair(FMT_PERSIST, "increment_size", "%lu", unit.size); util_fmt_obj_start(FMT_LIST, "partitions"); util_list_iterate(parts->list, cur) { util_fmt_obj_start(FMT_ROW, NULL); util_fmt_pair(FMT_DEFAULT, "partition_nr", "%u", cur->part_nr); util_fmt_pair(FMT_QUOTE, "partition_name", "%s", cur->part_name); parseable_generic_level(cur, 0, g.max_level, vdata->level_len); util_fmt_obj_end(); } util_fmt_obj_end(); util_fmt_obj_end(); free(tod); } static void parseable_csv_level(struct partition *part, unsigned int step, unsigned int level, unsigned int this_part, struct view_data *vdata, char *tod) { struct topology_entry *entries; unsigned int i, start, end; struct stride_unit unit; if (level < g.nesting_level) return; unit = vdata->unit; entries = &part->entries[level - 1]; start = step * vdata->level_len[level - 1]; end = (step + 1) * vdata->level_len[level - 1]; for (i = start; i < end; i++) { util_fmt_obj_start(FMT_ROW, NULL); util_fmt_pair(FMT_PERSIST, "report_tod", "%s", tod); util_fmt_pair(FMT_PERSIST, "report_partition_nr", "%u", this_part); util_fmt_pair(FMT_PERSIST, "increment_size", "%lu", unit.size); util_fmt_pair(FMT_DEFAULT, "partition_nr", "%u", part->part_nr); util_fmt_pair(FMT_DEFAULT, "partition_name", "%s", part->part_name); if (level == g.max_level) { util_fmt_pair(FMT_DEFAULT, "parent_level", "-"); util_fmt_pair(FMT_DEFAULT, "parent_entry_idx", "-"); } else { util_fmt_pair(FMT_DEFAULT, "parent_level", "%u", level + 1); util_fmt_pair(FMT_DEFAULT, "parent_entry_idx", "%u", step % vdata->level_len[level]); } util_fmt_pair(FMT_DEFAULT, "level", "%u", level); util_fmt_pair(FMT_DEFAULT, "entry_idx", "%u", i % vdata->level_len[level - 1]); util_fmt_pair(FMT_DEFAULT, "increment_count", "%u", entries->increments[i]); util_fmt_obj_end(); parseable_csv_level(part, i, level - 1, this_part, vdata, tod); } } static void parseable_csv(struct partitions *parts, struct view_data *vdata) { struct partition *cur; char *tod; tod = parseable_create_tod_hex(parts->tod); util_fmt_obj_start(FMT_DEFAULT, NULL); util_list_iterate(parts->list, cur) { parseable_csv_level(cur, 0, g.max_level, parts->this_part, vdata, tod); } util_fmt_obj_end(); free(tod); } static void print_parseable(struct partitions *parts) { struct view_data *vdata; vdata = util_zalloc(sizeof(*vdata)); vdata->unit = determine_stride_unit(); partition_list_calculate_level_lengths(parts, vdata); util_fmt_init(stdout, g.format, g.fmt_flags, 1); if (g.format == FMT_CSV) parseable_csv(parts, vdata); else parseable_generic(parts, vdata); util_fmt_exit(); free(vdata); } int main(int argc, char *argv[]) { struct partitions *parts; void *data; g.max_level = NESTING_LVL_MIN; g.nesting_level = NESTING_LVL_DEF; util_prg_init(&prg); util_opt_init(opt_vec, NULL); ebcdic_iconv_init(); parse_args(argc, argv); diag310_check_support(); data = diag310_get_memtop_data(); parts = partition_list_create(); partition_list_populate(data, parts); partition_list_sort(parts); if (g.fmt_specified) print_parseable(parts); else if (g.table_view) table_print(parts); else tree_print(parts); ebcdic_iconv_deinit(); partition_list_free(parts); free(data); return 0; }