diff options
author | Dan McDonald <danmcd@mnx.io> | 2022-11-10 11:06:38 -0500 |
---|---|---|
committer | Dan McDonald <danmcd@mnx.io> | 2022-11-10 11:06:38 -0500 |
commit | fe2789d05c8a7f2b216028f4b925f14ad6490f7e (patch) | |
tree | 2f6170d853de053c2c872d435b6496719a99b2d5 | |
parent | 758597efca5a7022fe4ed7c3b6653ed0068e795e (diff) | |
parent | 6eeafb34dceabceff80ed689002b6dc3e060f498 (diff) | |
download | illumos-joyent-fe2789d05c8a7f2b216028f4b925f14ad6490f7e.tar.gz |
[illumos-gate merge]
commit 6eeafb34dceabceff80ed689002b6dc3e060f498
15109 dtrace replicated mdb's bitfield mistakes
15111 dtrace -xtree doesn't always escape strings
commit 603778843038dfbc5672c2565d9ce3dac034609d
15110 mdb ::printf replicated mdb ::print bitfield mistakes
commit f651720770e11ade275c4fc451e6b00c9e5d3068
15126 ipv4info_t translator produces bad flags value
15113 dtrace ICMPv4 tests fail due to flags mismatch
commit 17425aa5357a01155862de9af35ff553bab2bb86
15121 tst.temporal.ksh has PATH dependencies
22 files changed, 504 insertions, 77 deletions
diff --git a/exception_lists/wscheck b/exception_lists/wscheck index 4b4465646c..638acb3f52 100644 --- a/exception_lists/wscheck +++ b/exception_lists/wscheck @@ -193,4 +193,4 @@ usr/src/lib/lib9p/common/* # These bits form the mdb test suite are all literal output from mdb and # therefore trailing spaces may be part of what mdb does today. # -usr/src/test/util-tests/tests/mdb/*/*.mdb.out +usr/src/test/util-tests/tests/mdb/*/*.*.out diff --git a/usr/src/cmd/dtrace/test/tst/common/Makefile b/usr/src/cmd/dtrace/test/tst/common/Makefile index 9ec706565a..14ba3acd19 100644 --- a/usr/src/cmd/dtrace/test/tst/common/Makefile +++ b/usr/src/cmd/dtrace/test/tst/common/Makefile @@ -28,6 +28,7 @@ # Copyright (c) 2012 by Delphix. All rights reserved. # Copyright 2015 Nexenta Systems, Inc. All rights reserved. # Copyright 2018 Joyent, Inc. +# Copyright 2022 Oxide Computer Company # # @@ -90,9 +91,16 @@ json/tst.usdt.exe: json/tst.usdt.o json/usdt.o $(POST_PROCESS) ; $(STRIP_STABS) # -# Tests that use the next three programs rely on the binaries having +# Tests that use the next four programs rely on the binaries having # valid CTF data. # +bitfields/tst.bitfields.exe: bitfields/tst.bitfields.c + $(COMPILE.c) $(CTF_FLAGS) -o bitfields/tst.bitfields.o bitfields/tst.bitfields.c + $(CTFCONVERT) -i -L VERSION bitfields/tst.bitfields.o + $(LINK.c) -o bitfields/tst.bitfields.exe bitfields/tst.bitfields.o $(LDLIBS) + $(CTFMERGE) -L VERSION -o $@ bitfields/tst.bitfields.o + $(POST_PROCESS) ; $(STRIP_STABS) + uctf/tst.aouttype.exe: uctf/tst.aouttype.c $(COMPILE.c) $(CTF_FLAGS) -o uctf/tst.aouttype.o uctf/tst.aouttype.c $(CTFCONVERT) -i -L VERSION uctf/tst.aouttype.o diff --git a/usr/src/cmd/dtrace/test/tst/common/bitfields/tst.bitfields.c b/usr/src/cmd/dtrace/test/tst/common/bitfields/tst.bitfields.c new file mode 100644 index 0000000000..180127191a --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/bitfields/tst.bitfields.c @@ -0,0 +1,92 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2022 Oxide Computer Company + */ + +/* + * This is designed to allow us to execute print() and various dereferencing + * operations with bitfields. It reads the values from argc and passes them to + * functions that we can then take apart. Crtitically this uses bitfields + * constructed via CTF and not the D compiler. + */ + +#include <err.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdlib.h> +#include <errno.h> + +typedef struct bit0 { + uint32_t a:3; + uint32_t b:2; + uint32_t c:1; + uint32_t d:1; + uint32_t e:1; + uint32_t f:1; + uint32_t g:3; + uint32_t h:3; + uint32_t i:5; + uint32_t j:4; + uint32_t k:6; + uint32_t l:1; + uint32_t m:1; +} bit0_t; + +typedef struct bit1 { + uint16_t a:1; + uint16_t b:8; + uint16_t c:3; + uint16_t d:2; + uint16_t e:1; + uint16_t f:1; +} bit1_t; + +void +mumble(FILE *f, bit0_t *zero, bit1_t *one) +{ + (void) fprintf(f, "%u\n%u\n", zero->k, one->d); +} + +int +main(int argc, char *argv[]) +{ + unsigned long l; + uint16_t u16; + uint32_t u32; + FILE *f; + + if (argc != 3) { + errx(EXIT_FAILURE, "Need two ints"); + } + + f = fopen("/dev/null", "rw+"); + if (f == NULL) { + err(EXIT_FAILURE, "failed to open /dev/null"); + } + + errno = 0; + l = strtoul(argv[1], NULL, 0); + if (errno != 0 || l == 0 || l > UINT16_MAX) { + errx(EXIT_FAILURE, "invalid u16 value: %s", argv[1]); + } + u16 = (uint16_t)l; + + l = strtoul(argv[2], NULL, 0); + if (errno != 0 || l == 0 || l > UINT32_MAX) { + errx(EXIT_FAILURE, "invalid u32 value: %s", argv[1]); + } + u32 = (uint32_t)l; + mumble(f, (bit0_t *)&u32, (bit1_t *)&u16); + + return (0); +} diff --git a/usr/src/cmd/dtrace/test/tst/common/bitfields/tst.bitfields.ksh b/usr/src/cmd/dtrace/test/tst/common/bitfields/tst.bitfields.ksh new file mode 100644 index 0000000000..b79046ba62 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/bitfields/tst.bitfields.ksh @@ -0,0 +1,63 @@ +#!/usr/bin/ksh +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2022 Oxide Computer Company +# + +# +# This test acts as a series of regression tests in DTrace around +# printing bitfields that are byte sized at non-byte aligned offsets and +# around printing fields that are less than a byte in size, but due to their +# offset, cross a byte boundary (e.g. a 5-bit bitfield that starts at +# bit 6). +# +# The DTrace implementation has two different general paths for this: +# +# o The D compiler compiling a dereference into DIF code to be executed +# in probe context to extract a bitfield value. +# o The print() action which grabs the larger chunk of memory and then +# processes it all in userland. +# + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +exe="tst.bitfields.exe" + +elfdump "./$exe" | grep -q '.SUNW_ctf' +if (( $? != 0 )); then + echo "CTF does not exist in $exe, that's a bug" >&2 + exit 1 +fi + +$dtrace -qs /dev/stdin -c "./$exe 0xe417 0x9391d7db" <<EOF +pid\$target::mumble:entry +{ + print(*args[1]); + printf("\n"); + print(*args[2]); + printf("\n"); + trace(args[2]->b); + printf("\n0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", args[2]->a, args[2]->b, + args[2]->c, args[2]->d, args[2]->e, args[2]->f); + trace(args[1]->i); + printf("\n0x%x 0x%x 0x%x 0x%x\n", args[1]->g, args[1]->h, args[1]->i, + args[1]->j); +} +EOF + +exit $rc diff --git a/usr/src/cmd/dtrace/test/tst/common/bitfields/tst.bitfields.ksh.out b/usr/src/cmd/dtrace/test/tst/common/bitfields/tst.bitfields.ksh.out new file mode 100644 index 0000000000..8db1736859 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/bitfields/tst.bitfields.ksh.out @@ -0,0 +1,28 @@ +bit0_t { + unsigned int:3 a :3 = 0x3 + unsigned int:2 b :2 = 0x3 + unsigned int:1 c :1 = 0 + unsigned int:1 d :1 = 0x1 + unsigned int:1 e :1 = 0x1 + unsigned int:1 f :1 = 0x1 + unsigned int:3 g :3 = 0x3 + unsigned int:3 h :3 = 0x5 + unsigned int:5 i :5 = 0x3 + unsigned int:4 j :4 = 0x9 + unsigned int:6 k :6 = 0x13 + unsigned int:1 l :1 = 0 + unsigned int:1 m :1 = 0x1 +} +bit1_t { + unsigned short:1 a :1 = 0x1 + unsigned short:8 b :8 = 0xb + unsigned short:3 c :3 = 0x2 + unsigned short:2 d :2 = 0x2 + unsigned short:1 e :1 = 0x1 + unsigned short:1 f :1 = 0x1 +} +11 +0x1 0xb 0x2 0x2 0x1 0x1 +3 +0x3 0x5 0x3 0x9 + diff --git a/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4localicmp.ksh b/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4localicmp.ksh index 0965040e62..d95a00c48a 100755 --- a/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4localicmp.ksh +++ b/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4localicmp.ksh @@ -24,7 +24,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#pragma ident "%Z%%M% %I% %E% SMI" # # Test ip:::{send,receive} of IPv4 ICMP to a local address. @@ -45,7 +44,7 @@ fi dtrace=$1 local=127.0.0.1 -$dtrace -c "/usr/sbin/ping $local 3" -qs /dev/stdin <<EOF | sort -n +$dtrace -c "/usr/sbin/ping -D $local 3" -qs /dev/stdin <<EOF | sort -n ip:::send /args[2]->ip_saddr == "$local" && args[2]->ip_daddr == "$local" && args[4]->ipv4_protocol == IPPROTO_ICMP/ diff --git a/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4localicmp.ksh.out b/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4localicmp.ksh.out index 41d6e0c8ad..ce028a4c86 100644 --- a/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4localicmp.ksh.out +++ b/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4localicmp.ksh.out @@ -1,6 +1,6 @@ -1 ip:::send (args[2]: 4 64, args[4]: 4 84 0 0 255) -1 ip:::send (args[2]: 4 64, args[4]: 4 84 0 0 255) -2 ip:::receive (args[2]: 4 64, args[4]: 4 84 0 0 255) -2 ip:::receive (args[2]: 4 64, args[4]: 4 84 0 0 255) +1 ip:::send (args[2]: 4 64, args[4]: 4 84 2 0 255) +1 ip:::send (args[2]: 4 64, args[4]: 4 84 2 0 255) +2 ip:::receive (args[2]: 4 64, args[4]: 4 84 2 0 255) +2 ip:::receive (args[2]: 4 64, args[4]: 4 84 2 0 255) 127.0.0.1 is alive diff --git a/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4remoteicmp.ksh b/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4remoteicmp.ksh index efe0d30fa9..b2aa601ad2 100755 --- a/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4remoteicmp.ksh +++ b/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4remoteicmp.ksh @@ -24,7 +24,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#pragma ident "%Z%%M% %I% %E% SMI" # # Test ip:::{send,receive} of IPv4 ICMP to a remote host. @@ -55,7 +54,7 @@ if (( $? != 0 )); then exit 4 fi -$dtrace -c "/usr/sbin/ping $dest 3" -qs /dev/stdin <<EOF | \ +$dtrace -c "/usr/sbin/ping -D $dest 3" -qs /dev/stdin <<EOF | \ grep -v 'is alive' | sort -n ip:::send /args[2]->ip_saddr == "$source" && args[2]->ip_daddr == "$dest" && diff --git a/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4remoteicmp.ksh.out b/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4remoteicmp.ksh.out index b5915f8db4..a330569d62 100644 --- a/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4remoteicmp.ksh.out +++ b/usr/src/cmd/dtrace/test/tst/common/ip/tst.ipv4remoteicmp.ksh.out @@ -1,3 +1,3 @@ -1 ip:::send (args[2]: 4 64, args[4]: 4 84 0 0 255) -2 ip:::receive (args[2]: 4 64, args[4]: 4 84 4 0 255) +1 ip:::send (args[2]: 4 64, args[4]: 4 84 2 0 255) +2 ip:::receive (args[2]: 4 64, args[4]: 4 84 2 0 255) diff --git a/usr/src/cmd/dtrace/test/tst/common/pragma/tst.temporal.ksh b/usr/src/cmd/dtrace/test/tst/common/pragma/tst.temporal.ksh index 9a0aed0678..eb8397478a 100644 --- a/usr/src/cmd/dtrace/test/tst/common/pragma/tst.temporal.ksh +++ b/usr/src/cmd/dtrace/test/tst/common/pragma/tst.temporal.ksh @@ -1,7 +1,5 @@ #!/bin/ksh -p # -# CDDL HEADER START -# # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version @@ -11,11 +9,10 @@ # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # -# CDDL HEADER END -# # # Copyright (c) 2012 by Delphix. All rights reserved. +# Copyright 2022 Oxide Computer Company # ############################################################################ @@ -74,7 +71,8 @@ fi # dtrace outputs a blank line at the end, which will sort to the beginning, # so use head to remove the blank line. -head -n -1 $file > $file.2 +nlines=$(wc -l $file | awk '{ print $1 - 1}') +head -n $nlines $file > $file.2 sort -n $file.2 | diff $file.2 - status=$? diff --git a/usr/src/cmd/mdb/common/mdb/mdb_print.c b/usr/src/cmd/mdb/common/mdb/mdb_print.c index a7c806ad29..9bd5028b08 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_print.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_print.c @@ -27,7 +27,7 @@ * Copyright (c) 2012, 2014 by Delphix. All rights reserved. * Copyright 2020 Joyent, Inc. * Copyright (c) 2014 Nexenta Systems, Inc. All rights reserved. - * Copyright 2021 Oxide Computer Company + * Copyright 2022 Oxide Computer Company */ #include <mdb/mdb_modapi.h> @@ -855,6 +855,28 @@ cmd_array(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) } /* + * This is a shared implementation to determine if we should treat a type as a + * bitfield. The parameters are the CTF encoding and the bit offset of the + * integer. This also exists in mdb_print.c. We consider something a bitfield + * if: + * + * o The type is more than 8 bytes. This is a bit of a historical choice from + * mdb and is a stranger one. The normal integer handling code generally + * doesn't handle integers more than 64-bits in size. Of course neither does + * the bitfield code... + * o The bit count is not a multiple of 8. + * o The size in bytes is not a power of 2. + * o The offset is not a multiple of 8. + */ +boolean_t +is_bitfield(const ctf_encoding_t *ep, ulong_t off) +{ + size_t bsize = ep->cte_bits / NBBY; + return (bsize > 8 || (ep->cte_bits % NBBY) != 0 || + (bsize & (bsize - 1)) != 0 || (off % NBBY) != 0); +} + +/* * Print an integer bitfield in hexadecimal by reading the enclosing byte(s) * and then shifting and masking the data in the lower bits of a uint64_t. */ @@ -862,14 +884,21 @@ static int print_bitfield(ulong_t off, printarg_t *pap, ctf_encoding_t *ep) { mdb_tgt_addr_t addr = pap->pa_addr + off / NBBY; - size_t size = (ep->cte_bits + (NBBY - 1)) / NBBY; uint64_t mask = (1ULL << ep->cte_bits) - 1; uint64_t value = 0; uint8_t *buf = (uint8_t *)&value; uint8_t shift; - const char *format; + /* + * Our bitfield may straddle a byte boundary. We explicitly take the + * offset of the bitfield within its byte into account when determining + * the overall amount of data to copy and mask off from the underlying + * data. + */ + uint_t nbits = ep->cte_bits + (off % NBBY); + size_t size = P2ROUNDUP(nbits, NBBY) / NBBY; + if (!(pap->pa_flags & PA_SHOWVAL)) return (0); @@ -878,17 +907,9 @@ print_bitfield(ulong_t off, printarg_t *pap, ctf_encoding_t *ep) return (0); } - /* - * Our bitfield may stradle a byte boundary, if so, the calculation of - * size may not correctly capture that. However, off is relative to the - * entire bitfield, so we first have to make that relative to the byte. - */ - if ((off % 8) + ep->cte_bits > NBBY * size) { - size++; - } - if (size > sizeof (value)) { mdb_printf("??? (total bitfield too large after alignment"); + return (0); } /* @@ -1002,14 +1023,8 @@ print_int_val(const char *type, ctf_encoding_t *ep, ulong_t off, return (0); } - /* - * If the size is not a power-of-two number of bytes in the range 1-8 or - * power-of-two number starts in the middle of a byte then we assume it - * is a bitfield and print it as such. - */ size = ep->cte_bits / NBBY; - if (size > 8 || (ep->cte_bits % NBBY) != 0 || (size & (size - 1) || - (off % NBBY) != 0) != 0) { + if (is_bitfield(ep, off)) { return (print_bitfield(off, pap, ep)); } @@ -1811,7 +1826,7 @@ static int pipe_print(mdb_ctf_id_t id, ulong_t off, void *data) { printarg_t *pap = data; - ssize_t size; + size_t size; static const char *const fsp[] = { "%#r", "%#r", "%#r", "%#llr" }; uintptr_t value; uintptr_t addr = pap->pa_addr + off / NBBY; @@ -1870,13 +1885,12 @@ again: * For immediate values, we just print out the value. */ size = e.cte_bits / NBBY; - if (size > 8 || (e.cte_bits % NBBY) != 0 || - (size & (size - 1)) != 0) { + if (is_bitfield(&e, off)) { return (print_bitfield(off, pap, &e)); } if (mdb_tgt_aread(pap->pa_tgt, pap->pa_as, &u.i8, size, - addr) != size) { + addr) != (size_t)size) { mdb_warn("failed to read %lu bytes at %p", (ulong_t)size, pap->pa_addr); return (-1); @@ -2627,7 +2641,7 @@ print_help(void) static int printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt, int sign) { - ssize_t size; + size_t size; mdb_ctf_id_t base; ctf_encoding_t e; @@ -2676,23 +2690,43 @@ printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt, int sign) * particular, see the comments there for an explanation of the * endianness differences in this code.) */ - if (size > 8 || (e.cte_bits % NBBY) != 0 || - (size & (size - 1)) != 0) { + if (is_bitfield(&e, off)) { uint64_t mask = (1ULL << e.cte_bits) - 1; uint64_t value = 0; uint8_t *buf = (uint8_t *)&value; uint8_t shift; + uint_t nbits; /* - * Round our size up one byte. + * Our bitfield may straddle a byte boundary. We explicitly take + * the offset of the bitfield within its byte into account when + * determining the overall amount of data to copy and mask off + * from the underlying data. */ - size = (e.cte_bits + (NBBY - 1)) / NBBY; + nbits = e.cte_bits + (off % NBBY); + size = P2ROUNDUP(nbits, NBBY) / NBBY; if (e.cte_bits > sizeof (value) * NBBY - 1) { mdb_printf("invalid bitfield size %u", e.cte_bits); return (DCMD_ABORT); } + /* + * Our bitfield may straddle a byte boundary, if so, the + * calculation of size may not correctly capture that. However, + * off is relative to the entire bitfield, so we first have to + * make that relative to the byte. + */ + if ((off % NBBY) + e.cte_bits > NBBY * size) { + size++; + } + + if (size > sizeof (value)) { + mdb_warn("??? (total bitfield too large after " + "alignment\n"); + return (DCMD_ABORT); + } + #ifdef _BIG_ENDIAN buf += sizeof (value) - size; off += e.cte_bits; diff --git a/usr/src/lib/libdtrace/common/dt_cg.c b/usr/src/lib/libdtrace/common/dt_cg.c index 9f3625e6ee..816b31daf7 100644 --- a/usr/src/lib/libdtrace/common/dt_cg.c +++ b/usr/src/lib/libdtrace/common/dt_cg.c @@ -28,6 +28,7 @@ /* * Copyright (c) 2012 by Delphix. All rights reserved. * Copyright 2017 Joyent, Inc. + * Copyright 2022 Oxide Computer Company */ #include <sys/types.h> @@ -160,15 +161,17 @@ dt_cg_load(dt_node_t *dnp, ctf_file_t *ctfp, ctf_id_t type) ssize_t size; /* - * If we're loading a bit-field, the size of our load is found by - * rounding cte_bits up to a byte boundary and then finding the - * nearest power of two to this value (see clp2(), above). + * If we're loading a bit-field, we find the power-of-two that spans the + * full value. To do this we count the number of bytes that contain a + * portion of the bit-field. */ if ((dnp->dn_flags & DT_NF_BITFIELD) && - ctf_type_encoding(ctfp, type, &e) != CTF_ERR) - size = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY); - else + ctf_type_encoding(ctfp, type, &e) != CTF_ERR) { + uint_t nbits = e.cte_bits + (dnp->dn_bitoff % NBBY); + size = clp2(P2ROUNDUP(nbits, NBBY) / NBBY); + } else { size = ctf_type_size(ctfp, type); + } if (size < 1 || size > 8 || (size & (size - 1)) != 0) { xyerror(D_UNKNOWN, "internal error -- cg cannot load " diff --git a/usr/src/lib/libdtrace/common/dt_ident.c b/usr/src/lib/libdtrace/common/dt_ident.c index 33f94eb9fd..6927132ada 100644 --- a/usr/src/lib/libdtrace/common/dt_ident.c +++ b/usr/src/lib/libdtrace/common/dt_ident.c @@ -23,6 +23,7 @@ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. * Copyright (c) 2013 Joyent, Inc. All rights reserved. + * Copyright 2022 Oxide Computer Company */ #include <sys/sysmacros.h> @@ -266,6 +267,7 @@ dt_idcook_func(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) if (strcmp(p1, "@") == 0 || strcmp(p1, "...") == 0) { isp->dis_args[i].dn_ctfp = NULL; isp->dis_args[i].dn_type = CTF_ERR; + isp->dis_args[i].dn_bitoff = 0; if (*p1 == '.') isp->dis_varargs = i; continue; diff --git a/usr/src/lib/libdtrace/common/dt_impl.h b/usr/src/lib/libdtrace/common/dt_impl.h index 988b7f6fb7..cdf70586b0 100644 --- a/usr/src/lib/libdtrace/common/dt_impl.h +++ b/usr/src/lib/libdtrace/common/dt_impl.h @@ -27,6 +27,7 @@ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2011, 2016 by Delphix. All rights reserved. + * Copyright 2022 Oxide Computer Company */ #ifndef _DT_IMPL_H @@ -166,7 +167,7 @@ typedef struct dt_ahash { } dt_ahash_t; typedef struct dt_aggregate { - dtrace_bufdesc_t dtat_buf; /* buf aggregation snapshot */ + dtrace_bufdesc_t dtat_buf; /* buf aggregation snapshot */ int dtat_flags; /* aggregate flags */ processorid_t dtat_ncpus; /* number of CPUs in aggregate */ processorid_t *dtat_cpus; /* CPUs in aggregate */ @@ -653,6 +654,8 @@ extern int dt_handle_setopt(dtrace_hdl_t *, dtrace_setoptdata_t *); extern int dt_lib_depend_add(dtrace_hdl_t *, dt_list_t *, const char *); extern dt_lib_depend_t *dt_lib_depend_lookup(dt_list_t *, const char *); +extern boolean_t dt_is_bitfield(const ctf_encoding_t *, ulong_t); + extern dt_pcb_t *yypcb; /* pointer to current parser control block */ extern char yyintprefix; /* int token prefix for macros (+/-) */ extern char yyintsuffix[4]; /* int token suffix ([uUlL]*) */ diff --git a/usr/src/lib/libdtrace/common/dt_parser.c b/usr/src/lib/libdtrace/common/dt_parser.c index 176d9b7d48..9cd9520f0f 100644 --- a/usr/src/lib/libdtrace/common/dt_parser.c +++ b/usr/src/lib/libdtrace/common/dt_parser.c @@ -23,6 +23,7 @@ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, Joyent Inc. All rights reserved. * Copyright (c) 2012, 2016 by Delphix. All rights reserved. + * Copyright 2022 Oxide Computer Company */ /* @@ -517,6 +518,7 @@ dt_node_xalloc(dtrace_hdl_t *dtp, int kind) dnp->dn_ctfp = NULL; dnp->dn_type = CTF_ERR; + dnp->dn_bitoff = 0; dnp->dn_kind = (uchar_t)kind; dnp->dn_flags = 0; dnp->dn_op = 0; @@ -671,8 +673,8 @@ dt_node_attr_assign(dt_node_t *dnp, dtrace_attribute_t attr) } void -dt_node_type_assign(dt_node_t *dnp, ctf_file_t *fp, ctf_id_t type, - boolean_t user) +dt_node_type_assign_member(dt_node_t *dnp, ctf_file_t *fp, ctf_id_t type, + boolean_t user, ulong_t bitoff) { ctf_id_t base = ctf_type_resolve(fp, type); uint_t kind = ctf_type_kind(fp, base); @@ -682,9 +684,7 @@ dt_node_type_assign(dt_node_t *dnp, ctf_file_t *fp, ctf_id_t type, ~(DT_NF_SIGNED | DT_NF_REF | DT_NF_BITFIELD | DT_NF_USERLAND); if (kind == CTF_K_INTEGER && ctf_type_encoding(fp, base, &e) == 0) { - size_t size = e.cte_bits / NBBY; - - if (size > 8 || (e.cte_bits % NBBY) != 0 || (size & (size - 1))) + if (dt_is_bitfield(&e, bitoff)) dnp->dn_flags |= DT_NF_BITFIELD; if (e.cte_format & CTF_INT_SIGNED) @@ -710,6 +710,15 @@ dt_node_type_assign(dt_node_t *dnp, ctf_file_t *fp, ctf_id_t type, dnp->dn_flags |= DT_NF_COOKED; dnp->dn_ctfp = fp; dnp->dn_type = type; + dnp->dn_bitoff = bitoff; +} + + +void +dt_node_type_assign(dt_node_t *dnp, ctf_file_t *fp, ctf_id_t type, + boolean_t user) +{ + return (dt_node_type_assign_member(dnp, fp, type, user, 0)); } void @@ -719,6 +728,7 @@ dt_node_type_propagate(const dt_node_t *src, dt_node_t *dst) dst->dn_flags = src->dn_flags & ~DT_NF_LVALUE; dst->dn_ctfp = src->dn_ctfp; dst->dn_type = src->dn_type; + dst->dn_bitoff = src->dn_bitoff; } const char * @@ -1369,7 +1379,8 @@ dt_node_type(dt_decl_t *ddp) dnp->dn_op = DT_TOK_IDENT; dnp->dn_string = name; - dt_node_type_assign(dnp, dtt.dtt_ctfp, dtt.dtt_type, dtt.dtt_flags); + dt_node_type_assign(dnp, dtt.dtt_ctfp, dtt.dtt_type, + (dtt.dtt_flags & DTT_FL_USER) != 0); if (dtt.dtt_ctfp == dtp->dt_cdefs->dm_ctfp || dtt.dtt_ctfp == dtp->dt_ddefs->dm_ctfp) @@ -1392,6 +1403,7 @@ dt_node_vatype(void) dnp->dn_op = DT_TOK_IDENT; dnp->dn_ctfp = yypcb->pcb_hdl->dt_cdefs->dm_ctfp; dnp->dn_type = CTF_ERR; + dnp->dn_bitoff = 0; dnp->dn_attr = _dtrace_defattr; return (dnp); @@ -3767,7 +3779,8 @@ asgn_common: type = ctf_type_resolve(ctfp, m.ctm_type); kind = ctf_type_kind(ctfp, type); - dt_node_type_assign(dnp, ctfp, m.ctm_type, B_FALSE); + dt_node_type_assign_member(dnp, ctfp, m.ctm_type, B_FALSE, + m.ctm_offset); dt_node_attr_assign(dnp, lp->dn_attr); if (op == DT_TOK_PTR && (kind != CTF_K_ARRAY || @@ -4137,7 +4150,7 @@ dt_cook_aggregation(dt_node_t *dnp, uint_t idflags) * probe-description-list * /x++ && y == 0/ * { - * trace(x + y++); + * trace(x + y++); * } * * but it doesn't seem worth the complexity to handle such rare cases. The @@ -4842,9 +4855,12 @@ dt_node_printr(dt_node_t *dnp, FILE *fp, int depth) (u_longlong_t)dnp->dn_value, buf); break; - case DT_NODE_STRING: - (void) fprintf(fp, "STRING \"%s\" (%s)\n", dnp->dn_string, buf); + case DT_NODE_STRING: { + char *escd = strchr2esc(dnp->dn_string, strlen(dnp->dn_string)); + (void) fprintf(fp, "STRING \"%s\" (%s)\n", escd, buf); + free(escd); break; + } case DT_NODE_IDENT: (void) fprintf(fp, "IDENT %s (%s)\n", dnp->dn_string, buf); diff --git a/usr/src/lib/libdtrace/common/dt_parser.h b/usr/src/lib/libdtrace/common/dt_parser.h index f5f60a518e..96022ac5c1 100644 --- a/usr/src/lib/libdtrace/common/dt_parser.h +++ b/usr/src/lib/libdtrace/common/dt_parser.h @@ -25,6 +25,7 @@ /* * Copyright (c) 2013, 2016 by Delphix. All rights reserved. * Copyright (c) 2013 Joyent, Inc. All rights reserved. + * Copyright 2022 Oxide Computer Company */ #ifndef _DT_PARSER_H @@ -50,6 +51,7 @@ extern "C" { typedef struct dt_node { ctf_file_t *dn_ctfp; /* CTF type container for node's type */ ctf_id_t dn_type; /* CTF type reference for node's type */ + ulong_t dn_bitoff; /* Offset to the start of the CTF type */ uchar_t dn_kind; /* node kind (DT_NODE_*, defined below) */ uchar_t dn_flags; /* node flags (DT_NF_*, defined below) */ ushort_t dn_op; /* operator (DT_TOK_*, defined by lex) */ @@ -239,6 +241,8 @@ extern void dt_node_link_free(dt_node_t **); extern void dt_node_attr_assign(dt_node_t *, dtrace_attribute_t); extern void dt_node_type_assign(dt_node_t *, ctf_file_t *, ctf_id_t, boolean_t); +extern void dt_node_type_assign_bitfield(dt_node_t *, ctf_file_t *, ctf_id_t, + boolean_t, ulong_t); extern void dt_node_type_propagate(const dt_node_t *, dt_node_t *); extern const char *dt_node_type_name(const dt_node_t *, char *, size_t); extern size_t dt_node_type_size(const dt_node_t *); diff --git a/usr/src/lib/libdtrace/common/dt_print.c b/usr/src/lib/libdtrace/common/dt_print.c index b71eddbf9d..4b4a23b199 100644 --- a/usr/src/lib/libdtrace/common/dt_print.c +++ b/usr/src/lib/libdtrace/common/dt_print.c @@ -27,6 +27,7 @@ */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright 2022 Oxide Computer Company */ /* @@ -41,13 +42,13 @@ * * This implementation differs from MDB's in the following ways: * - * - We do not expose any options or flags. The behavior of print() is + * - We do not expose any options or flags. The behavior of print() is * equivalent to "::print -tn". * - * - MDB will display "holes" in structures (unused padding between + * - MDB will display "holes" in structures (unused padding between * members). * - * - When printing arrays of structures, MDB will leave a trailing ',' + * - When printing arrays of structures, MDB will leave a trailing ',' * after the last element. * * - MDB will print time_t types as date and time. @@ -149,7 +150,8 @@ dt_print_indent(dt_printarg_t *pap) * Print a bitfield. It's worth noting that the D compiler support for * bitfields is currently broken; printing "D`user_desc_t" (pulled in by the * various D provider files) will produce incorrect results compared to - * "genunix`user_desc_t". + * "genunix`user_desc_t". However, bitfields that are built via CTF will be + * fine. Note, this is derived from mdb's print_bifield in mdb_print.c. */ static void print_bitfield(dt_printarg_t *pap, ulong_t off, ctf_encoding_t *ep) @@ -158,11 +160,32 @@ print_bitfield(dt_printarg_t *pap, ulong_t off, ctf_encoding_t *ep) caddr_t addr = pap->pa_addr + off / NBBY; uint64_t mask = (1ULL << ep->cte_bits) - 1; uint64_t value = 0; - size_t size = (ep->cte_bits + (NBBY - 1)) / NBBY; uint8_t *buf = (uint8_t *)&value; uint8_t shift; /* + * Our bitfield may straddle a byte boundary. We explicitly take the + * offset of the bitfield within its byte into account when determining + * the overall amount of data to copy and mask off from the underlying + * data. + */ + uint_t nbits = ep->cte_bits + (off % NBBY); + size_t size = P2ROUNDUP(nbits, NBBY) / NBBY; + + /* + * The resulting size must fit within the 64-bit value that we're using + * to store the value, otherwise the bcopy below could destroy our + * stack. This could be handled, but is not practically worth + * addressing. This choice apes mdb, so if you're considering fixing + * this, fix mdb as well. + */ + if (size > sizeof (value)) { + (void) fprintf(fp, "??? (total bitfield too large after " + "alignment"); + return; + } + + /* * On big-endian machines, we need to adjust the buf pointer to refer * to the lowest 'size' bytes in 'value', and we need to shift based on * the offset from the end of the data, not the offset of the start. @@ -248,12 +271,8 @@ dt_print_int(ctf_id_t base, ulong_t off, dt_printarg_t *pap) return; } - /* - * We print this as a bitfield if the bit encoding indicates it's not - * an even power of two byte size, or is larger than 8 bytes. - */ size = e.cte_bits / NBBY; - if (size > 8 || (e.cte_bits % NBBY) != 0 || (size & (size - 1)) != 0) { + if (dt_is_bitfield(&e, off)) { print_bitfield(pap, off, &e); return; } @@ -341,7 +360,7 @@ dt_print_ptr(ctf_id_t base, ulong_t off, dt_printarg_t *pap) * each member, and recursively invoke ctf_type_visit() for each member. If * the members are non-structs, then we print them out directly: * - * [ 0x14, 0x2e, 0 ] + * [ 0x14, 0x2e, 0 ] * * If they are structs, then we print out the necessary leading and trailing * braces, to end up with: @@ -408,7 +427,7 @@ dt_print_array(ctf_id_t base, ulong_t off, dt_printarg_t *pap) * don't bother printing out the brackets. This lets print("foo") look * like: * - * string "foo" + * string "foo" * * As D will internally represent this as a char[256] array. */ diff --git a/usr/src/lib/libdtrace/common/dt_subr.c b/usr/src/lib/libdtrace/common/dt_subr.c index e93617ef59..1958e62f82 100644 --- a/usr/src/lib/libdtrace/common/dt_subr.c +++ b/usr/src/lib/libdtrace/common/dt_subr.c @@ -22,6 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright 2022 Oxide Computer Company */ #include <sys/sysmacros.h> @@ -909,3 +910,25 @@ dtrace_uaddr2str(dtrace_hdl_t *dtp, pid_t pid, return (dt_string2str(c, str, nbytes)); } + +/* + * This is a shared implementation to determine if we should treat a type as a + * bitfield. The parameters are the CTF encoding and the bit offset of the + * integer. This also exists in mdb_print.c. We consider something a bitfield + * if: + * + * o The type is more than 8 bytes. This is a bit of a historical choice from + * mdb and is a stranger one. The normal integer handling code generally + * doesn't handle integers more than 64-bits in size. Of course neither does + * the bitfield code... + * o The bit count is not a multiple of 8. + * o The size in bytes is not a power of 2. + * o The offset is not a multiple of 8. + */ +boolean_t +dt_is_bitfield(const ctf_encoding_t *ep, ulong_t off) +{ + size_t bsize = ep->cte_bits / NBBY; + return (bsize > 8 || (ep->cte_bits % NBBY) != 0 || + (bsize & (bsize - 1)) != 0 || (off % NBBY) != 0); +} diff --git a/usr/src/lib/libdtrace/common/ip.d.in b/usr/src/lib/libdtrace/common/ip.d.in index f1e6332529..7dd4d2a314 100644 --- a/usr/src/lib/libdtrace/common/ip.d.in +++ b/usr/src/lib/libdtrace/common/ip.d.in @@ -311,9 +311,9 @@ translator ipv4info_t < ipha_t *I > { ipv4_length = I != NULL ? ntohs(I->ipha_length) : 0; ipv4_ident = I != NULL ? ntohs(I->ipha_ident) : 0; ipv4_flags = I != NULL ? ntohs(I->ipha_fragment_offset_and_flags) >> - 12 : 0; + 13 : 0; ipv4_offset = I != NULL ? ntohs(I->ipha_fragment_offset_and_flags) & - 0x0fff : 0; + 0x1fff : 0; ipv4_ttl = I != NULL ? I->ipha_ttl : 0; ipv4_protocol = I != NULL ? I->ipha_protocol : 0; ipv4_protostr = I == NULL ? "<null>" : diff --git a/usr/src/pkg/manifests/system-dtrace-tests.p5m b/usr/src/pkg/manifests/system-dtrace-tests.p5m index 10e3ab672c..3edfb242dc 100644 --- a/usr/src/pkg/manifests/system-dtrace-tests.p5m +++ b/usr/src/pkg/manifests/system-dtrace-tests.p5m @@ -352,6 +352,9 @@ file \ mode=0444 file path=opt/SUNWdtrt/tst/common/bitfields/tst.BitFieldPromotion.d mode=0444 file path=opt/SUNWdtrt/tst/common/bitfields/tst.SizeofBitField.d mode=0444 +file path=opt/SUNWdtrt/tst/common/bitfields/tst.bitfields.exe mode=0555 +file path=opt/SUNWdtrt/tst/common/bitfields/tst.bitfields.ksh mode=0444 +file path=opt/SUNWdtrt/tst/common/bitfields/tst.bitfields.ksh.out mode=0444 dir path=opt/SUNWdtrt/tst/common/buffering file path=opt/SUNWdtrt/tst/common/buffering/err.end.d mode=0444 file path=opt/SUNWdtrt/tst/common/buffering/err.resize1.d mode=0444 diff --git a/usr/src/test/util-tests/tests/mdb/numbers/tst.bitfields.ksh b/usr/src/test/util-tests/tests/mdb/numbers/tst.bitfields.ksh index 3f39acae92..971c798053 100644 --- a/usr/src/test/util-tests/tests/mdb/numbers/tst.bitfields.ksh +++ b/usr/src/test/util-tests/tests/mdb/numbers/tst.bitfields.ksh @@ -12,7 +12,7 @@ # # -# Copyright 2021 Oxide Computer Company +# Copyright 2022 Oxide Computer Company # # @@ -26,5 +26,81 @@ tst_prog="$tst_root/progs/bitfields" tst_outfile="/tmp/mdb.bitfield.out.$$" tst_exp="$0.out" +# +# Top level ::print +# $MDB -e "first::print -t broken_t" $tst_prog > $ODIR/stdout $MDB -e "second::print -t broken6491_t" $tst_prog >> $ODIR/stdout + +# +# ::print of specific members +# +$MDB -e "first::print broken_t brk_a" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_b" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_c" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_d" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_e" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_f" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_g" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_h" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_i" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_j" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_k" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_l" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_m" $tst_prog >> $ODIR/stdout +$MDB -e "second::print broken6491_t a" $tst_prog >> $ODIR/stdout +$MDB -e "second::print broken6491_t b" $tst_prog >> $ODIR/stdout +$MDB -e "second::print broken6491_t c" $tst_prog >> $ODIR/stdout +$MDB -e "second::print broken6491_t d" $tst_prog >> $ODIR/stdout +$MDB -e "second::print broken6491_t e" $tst_prog >> $ODIR/stdout +$MDB -e "second::print broken6491_t f" $tst_prog >> $ODIR/stdout + +# +# ::printf of members. Note, if ::printf said '%x\n' below then we would +# include the string "\n" (not a newline) in the output. Instead we rely +# upon the implicit newline from mdb -e. +# +$MDB -e "first::printf '%x' broken_t brk_a" $tst_prog >> $ODIR/stdout +$MDB -e "first::printf '%x' broken_t brk_b" $tst_prog >> $ODIR/stdout +$MDB -e "first::printf '%x' broken_t brk_c" $tst_prog >> $ODIR/stdout +$MDB -e "first::printf '%x' broken_t brk_d" $tst_prog >> $ODIR/stdout +$MDB -e "first::printf '%x' broken_t brk_e" $tst_prog >> $ODIR/stdout +$MDB -e "first::printf '%x' broken_t brk_f" $tst_prog >> $ODIR/stdout +$MDB -e "first::printf '%x' broken_t brk_g" $tst_prog >> $ODIR/stdout +$MDB -e "first::printf '%x' broken_t brk_h" $tst_prog >> $ODIR/stdout +$MDB -e "first::printf '%x' broken_t brk_i" $tst_prog >> $ODIR/stdout +$MDB -e "first::printf '%x' broken_t brk_j" $tst_prog >> $ODIR/stdout +$MDB -e "first::printf '%x' broken_t brk_k" $tst_prog >> $ODIR/stdout +$MDB -e "first::printf '%x' broken_t brk_l" $tst_prog >> $ODIR/stdout +$MDB -e "first::printf '%x' broken_t brk_m" $tst_prog >> $ODIR/stdout +$MDB -e "second::printf '%x' broken6491_t a" $tst_prog >> $ODIR/stdout +$MDB -e "second::printf '%x' broken6491_t b" $tst_prog >> $ODIR/stdout +$MDB -e "second::printf '%x' broken6491_t c" $tst_prog >> $ODIR/stdout +$MDB -e "second::printf '%x' broken6491_t d" $tst_prog >> $ODIR/stdout +$MDB -e "second::printf '%x' broken6491_t e" $tst_prog >> $ODIR/stdout +$MDB -e "second::printf '%x' broken6491_t f" $tst_prog >> $ODIR/stdout + +# +# If we pipe the output of ::print that is a different bitfield logic +# path. So we take that all to a `::eval '.=K'` as a basic way to get it +# out. +# +$MDB -e "first::print broken_t brk_a | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_b | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_c | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_d | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_e | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_f | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_g | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_h | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_i | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_j | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_k | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_l | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "first::print broken_t brk_m | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "second::print broken6491_t a | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "second::print broken6491_t b | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "second::print broken6491_t c | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "second::print broken6491_t d | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "second::print broken6491_t e | ::eval '.=K'" $tst_prog >> $ODIR/stdout +$MDB -e "second::print broken6491_t f | ::eval '.=K'" $tst_prog >> $ODIR/stdout diff --git a/usr/src/test/util-tests/tests/mdb/numbers/tst.bitfields.ksh.out b/usr/src/test/util-tests/tests/mdb/numbers/tst.bitfields.ksh.out index 347415a638..b71fd45c60 100644 --- a/usr/src/test/util-tests/tests/mdb/numbers/tst.bitfields.ksh.out +++ b/usr/src/test/util-tests/tests/mdb/numbers/tst.bitfields.ksh.out @@ -21,3 +21,60 @@ broken6491_t { unsigned short:1 e :1 = 0x1 unsigned short:1 f :1 = 0 } +brk_a = 0x3 +brk_b = 0x3 +brk_c = 0 +brk_d = 0x1 +brk_e = 0x1 +brk_f = 0x1 +brk_g = 0x3 +brk_h = 0x5 +brk_i = 0x3 +brk_j = 0x9 +brk_k = 0x13 +brk_l = 0 +brk_m = 0x1 +a = 0x1 +b = 0x2 +c = 0 +d = 0 +e = 0x1 +f = 0 +3 +3 +0 +1 +1 +1 +3 +5 +3 +9 +13 +0 +1 +1 +2 +0 +0 +1 +0 + 3 + 3 + 0 + 1 + 1 + 1 + 3 + 5 + 3 + 9 + 13 + 0 + 1 + 1 + 2 + 0 + 0 + 1 + 0 |