From f497f9fe231e0e400f339c84a7d80c4aae2ac4d5 Mon Sep 17 00:00:00 2001 From: "Joshua M. Clulow" Date: Tue, 27 Nov 2012 08:55:56 +0000 Subject: [PATCH] 4477 DTrace should speak JSON Reviewed by: Bryan Cantrill Approved by: Gordon Ross --- usr/src/cmd/dtrace/test/tst/common/Makefile | 13 + usr/src/cmd/dtrace/test/tst/common/aggs/tst.subr.d | 3 + .../cmd/dtrace/test/tst/common/json/tst.general.d | 179 +++++++ .../dtrace/test/tst/common/json/tst.general.d.out | 218 ++++++++ .../cmd/dtrace/test/tst/common/json/tst.strsize.d | 51 ++ .../dtrace/test/tst/common/json/tst.strsize.d.out | 13 + usr/src/cmd/dtrace/test/tst/common/json/tst.usdt.c | 61 +++ usr/src/cmd/dtrace/test/tst/common/json/tst.usdt.d | 65 +++ .../cmd/dtrace/test/tst/common/json/tst.usdt.d.out | 11 + usr/src/cmd/dtrace/test/tst/common/json/usdt.d | 27 + .../test/tst/common/privs/tst.func_access.ksh | 9 +- .../test/tst/common/strtoll/err.BaseTooLarge.d | 35 ++ .../test/tst/common/strtoll/err.BaseTooSmall.d | 34 ++ .../dtrace/test/tst/common/strtoll/tst.strtoll.d | 66 +++ .../test/tst/common/strtoll/tst.strtoll.d.out | 20 + usr/src/common/util/strtolctype.h | 4 + usr/src/lib/libdtrace/common/dt_open.c | 10 +- usr/src/pkg/manifests/system-dtrace-tests.mf | 13 + usr/src/uts/common/dtrace/dtrace.c | 593 +++++++++++++++++++++ usr/src/uts/common/sys/dtrace.h | 4 +- usr/src/uts/intel/dtrace/Makefile | 2 + usr/src/uts/sparc/dtrace/Makefile | 1 + 22 files changed, 1427 insertions(+), 5 deletions(-) create mode 100644 usr/src/cmd/dtrace/test/tst/common/json/tst.general.d create mode 100644 usr/src/cmd/dtrace/test/tst/common/json/tst.general.d.out create mode 100644 usr/src/cmd/dtrace/test/tst/common/json/tst.strsize.d create mode 100644 usr/src/cmd/dtrace/test/tst/common/json/tst.strsize.d.out create mode 100644 usr/src/cmd/dtrace/test/tst/common/json/tst.usdt.c create mode 100644 usr/src/cmd/dtrace/test/tst/common/json/tst.usdt.d create mode 100644 usr/src/cmd/dtrace/test/tst/common/json/tst.usdt.d.out create mode 100644 usr/src/cmd/dtrace/test/tst/common/json/usdt.d create mode 100644 usr/src/cmd/dtrace/test/tst/common/strtoll/err.BaseTooLarge.d create mode 100644 usr/src/cmd/dtrace/test/tst/common/strtoll/err.BaseTooSmall.d create mode 100644 usr/src/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d create mode 100644 usr/src/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d.out diff --git a/usr/src/cmd/dtrace/test/tst/common/Makefile b/usr/src/cmd/dtrace/test/tst/common/Makefile index 4b813cb231..147e66b7ad 100644 --- a/usr/src/cmd/dtrace/test/tst/common/Makefile +++ b/usr/src/cmd/dtrace/test/tst/common/Makefile @@ -26,6 +26,7 @@ # # Copyright (c) 2012 by Delphix. All rights reserved. +# Copyright (c) 2012, Joyent, Inc. All rights reserved. # include $(SRC)/Makefile.master @@ -72,6 +73,18 @@ pid/tst.gcc.exe: pid/tst.gcc.c $(GCC) -o pid/tst.gcc.exe pid/tst.gcc.c $(LDFLAGS) $(POST_PROCESS) ; $(STRIP_STABS) +json/tst.usdt.o: json/usdt.h + +json/usdt.h: json/usdt.d + $(DTRACE) -h -s json/usdt.d -o json/usdt.h + +json/usdt.o: json/usdt.d json/tst.usdt.o + $(COMPILE.d) -o json/usdt.o -s json/usdt.d json/tst.usdt.o + +json/tst.usdt.exe: json/tst.usdt.o json/usdt.o + $(LINK.c) -o json/tst.usdt.exe json/tst.usdt.o json/usdt.o $(LDLIBS) + $(POST_PROCESS) ; $(STRIP_STABS) + usdt/tst.args.exe: usdt/tst.args.o usdt/args.o $(LINK.c) -o usdt/tst.args.exe usdt/tst.args.o usdt/args.o $(LDLIBS) $(POST_PROCESS) ; $(STRIP_STABS) diff --git a/usr/src/cmd/dtrace/test/tst/common/aggs/tst.subr.d b/usr/src/cmd/dtrace/test/tst/common/aggs/tst.subr.d index b9b1ebdb49..ccafc2edd1 100644 --- a/usr/src/cmd/dtrace/test/tst/common/aggs/tst.subr.d +++ b/usr/src/cmd/dtrace/test/tst/common/aggs/tst.subr.d @@ -22,6 +22,7 @@ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ #include @@ -99,6 +100,8 @@ STRFUNC(inet_ntop(AF_INET, (void *)alloca(sizeof (ipaddr_t)))) STRFUNC(toupper("foo")) STRFUNC(tolower("BAR")) INTFUNC(getf(0)) +INTFUNC(strtoll("0x12EE5D5", 16)) +STRFUNC(json("{\"systemtap\": false}", "systemtap")) BEGIN /subr == DIF_SUBR_MAX + 1/ diff --git a/usr/src/cmd/dtrace/test/tst/common/json/tst.general.d b/usr/src/cmd/dtrace/test/tst/common/json/tst.general.d new file mode 100644 index 0000000000..4600811d95 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/json/tst.general.d @@ -0,0 +1,179 @@ +/* + * 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 2012, Joyent, Inc. All rights reserved. + */ + +/* + * General functional tests of JSON parser for json(). + */ + +#pragma D option quiet +#pragma D option strsize=1k + +#define TST(name) \ + printf("\ntst |%s|\n", name) +#define IN2(vala, valb) \ + in = strjoin(vala, valb); \ + printf("in |%s|\n", in) +#define IN(val) \ + in = val; \ + printf("in |%s|\n", in) +#define SEL(ss) \ + out = json(in, ss); \ + printf("sel |%s|\nout |%s|\n", ss, \ + out != NULL ? out : "") + +BEGIN +{ + TST("empty array"); + IN("[]"); + SEL("0"); + + TST("one-element array: integer"); + IN("[1]"); + SEL("0"); + SEL("1"); + SEL("100"); + SEL("-1"); + + TST("one-element array: hex integer (not in spec, not supported)"); + IN("[0x1000]"); + SEL("0"); + + TST("one-element array: float"); + IN("[1.5001]"); + SEL("0"); + + TST("one-element array: float + exponent"); + IN("[16.3e10]"); + SEL("0"); + + TST("one-element array: integer + whitespace"); + IN("[ \t 5\t]"); + SEL("0"); + + TST("one-element array: integer + exponent + whitespace"); + IN("[ \t \t 16E10 \t ]"); + SEL("0"); + + TST("one-element array: string"); + IN("[\"alpha\"]"); + SEL("0"); + + TST("alternative first-element indexing"); + IN("[1,5,10,15,20]"); + SEL("[0]"); + SEL("[3]"); + SEL("[4]"); + SEL("[5]"); + + TST("one-element array: object"); + IN("[ { \"first\": true, \"second\": false }]"); + SEL("0.first"); + SEL("0.second"); + SEL("0.third"); + + TST("many-element array: integers"); + IN("[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377]"); + SEL("10"); /* F(10) = 55 */ + SEL("14"); /* F(14) = 377 */ + SEL("19"); + + TST("many-element array: multiple types"); + IN2("[\"string\",32,true,{\"a\":9,\"b\":false},100.3e10,false,200.5,", + "{\"key\":\"val\"},null]"); + SEL("0"); + SEL("0.notobject"); + SEL("1"); + SEL("2"); + SEL("3"); + SEL("3.a"); + SEL("3.b"); + SEL("3.c"); + SEL("4"); + SEL("5"); + SEL("6"); + SEL("7"); + SEL("7.key"); + SEL("7.key.notobject"); + SEL("7.nonexist"); + SEL("8"); + SEL("9"); + + TST("many-element array: multiple types + whitespace"); + IN2("\n[\t\"string\" ,\t32 , true\t,\t {\"a\": 9,\t\"b\": false},\t\t", + "100.3e10, false, 200.5,{\"key\" \t:\n \"val\"},\t\t null ]\t\t"); + SEL("0"); + SEL("0.notobject"); + SEL("1"); + SEL("2"); + SEL("3"); + SEL("3.a"); + SEL("3.b"); + SEL("3.c"); + SEL("4"); + SEL("5"); + SEL("6"); + SEL("7"); + SEL("7.key"); + SEL("7.key.notobject"); + SEL("7.nonexist"); + SEL("8"); + SEL("9"); + + TST("two-element array: various string escape codes"); + IN2("[\"abcd \\\" \\\\ \\/ \\b \\f \\n \\r \\t \\u0000 \\uf00F \", ", + "\"final\"]"); + SEL("0"); + SEL("1"); + + TST("three-element array: broken escape code"); + IN("[\"fine here\", \"dodgey \\u00AZ\", \"wont get here\"]"); + SEL("0"); + SEL("1"); + SEL("2"); + + TST("nested objects"); + IN2("{ \"top\": { \"mid\" : { \"legs\": \"feet\" }, \"number\": 9, ", + "\"array\":[0,1,{\"a\":true,\"bb\":[1,2,false,{\"x\":\"yz\"}]}]}}"); + SEL("top"); + SEL("fargo"); + SEL("top.mid"); + SEL("top.centre"); + SEL("top.mid.legs"); + SEL("top.mid.number"); + SEL("top.mid.array"); + SEL("top.number"); + SEL("top.array"); + SEL("top.array[0]"); + SEL("top.array[1]"); + SEL("top.array[2]"); + SEL("top.array[2].a"); + SEL("top.array[2].b"); + SEL("top.array[2].bb"); + SEL("top.array[2].bb[0]"); + SEL("top.array[2].bb[1]"); + SEL("top.array[2].bb[2]"); + SEL("top.array[2].bb[3]"); + SEL("top.array[2].bb[3].x"); + SEL("top.array[2].bb[3].x.nofurther"); + SEL("top.array[2].bb[4]"); + SEL("top.array[3]"); + + exit(0); +} + +ERROR +{ + exit(1); +} diff --git a/usr/src/cmd/dtrace/test/tst/common/json/tst.general.d.out b/usr/src/cmd/dtrace/test/tst/common/json/tst.general.d.out new file mode 100644 index 0000000000..a857ab91d8 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/json/tst.general.d.out @@ -0,0 +1,218 @@ + +tst |empty array| +in |[]| +sel |0| +out || + +tst |one-element array: integer| +in |[1]| +sel |0| +out |1| +sel |1| +out || +sel |100| +out || +sel |-1| +out || + +tst |one-element array: hex integer (not in spec, not supported)| +in |[0x1000]| +sel |0| +out || + +tst |one-element array: float| +in |[1.5001]| +sel |0| +out |1.5001| + +tst |one-element array: float + exponent| +in |[16.3e10]| +sel |0| +out |16.3e10| + +tst |one-element array: integer + whitespace| +in |[ 5 ]| +sel |0| +out |5| + +tst |one-element array: integer + exponent + whitespace| +in |[ 16E10 ]| +sel |0| +out |16E10| + +tst |one-element array: string| +in |["alpha"]| +sel |0| +out |alpha| + +tst |alternative first-element indexing| +in |[1,5,10,15,20]| +sel |[0]| +out |1| +sel |[3]| +out |15| +sel |[4]| +out |20| +sel |[5]| +out || + +tst |one-element array: object| +in |[ { "first": true, "second": false }]| +sel |0.first| +out |true| +sel |0.second| +out |false| +sel |0.third| +out || + +tst |many-element array: integers| +in |[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377]| +sel |10| +out |55| +sel |14| +out |377| +sel |19| +out || + +tst |many-element array: multiple types| +in |["string",32,true,{"a":9,"b":false},100.3e10,false,200.5,{"key":"val"},null]| +sel |0| +out |string| +sel |0.notobject| +out || +sel |1| +out |32| +sel |2| +out |true| +sel |3| +out |{"a":9,"b":false}| +sel |3.a| +out |9| +sel |3.b| +out |false| +sel |3.c| +out || +sel |4| +out |100.3e10| +sel |5| +out |false| +sel |6| +out |200.5| +sel |7| +out |{"key":"val"}| +sel |7.key| +out |val| +sel |7.key.notobject| +out || +sel |7.nonexist| +out || +sel |8| +out |null| +sel |9| +out || + +tst |many-element array: multiple types + whitespace| +in | +[ "string" , 32 , true , {"a": 9, "b": false}, 100.3e10, false, 200.5,{"key" : + "val"}, null ] | +sel |0| +out |string| +sel |0.notobject| +out || +sel |1| +out |32| +sel |2| +out |true| +sel |3| +out |{"a": 9, "b": false}| +sel |3.a| +out |9| +sel |3.b| +out |false| +sel |3.c| +out || +sel |4| +out |100.3e10| +sel |5| +out |false| +sel |6| +out |200.5| +sel |7| +out |{"key" : + "val"}| +sel |7.key| +out |val| +sel |7.key.notobject| +out || +sel |7.nonexist| +out || +sel |8| +out |null| +sel |9| +out || + +tst |two-element array: various string escape codes| +in |["abcd \" \\ \/ \b \f \n \r \t \u0000 \uf00F ", "final"]| +sel |0| +out |abcd \" \\ \/ \b \f \n \r \t \u0000 \uf00F | +sel |1| +out |final| + +tst |three-element array: broken escape code| +in |["fine here", "dodgey \u00AZ", "wont get here"]| +sel |0| +out |fine here| +sel |1| +out || +sel |2| +out || + +tst |nested objects| +in |{ "top": { "mid" : { "legs": "feet" }, "number": 9, "array":[0,1,{"a":true,"bb":[1,2,false,{"x":"yz"}]}]}}| +sel |top| +out |{ "mid" : { "legs": "feet" }, "number": 9, "array":[0,1,{"a":true,"bb":[1,2,false,{"x":"yz"}]}]}| +sel |fargo| +out || +sel |top.mid| +out |{ "legs": "feet" }| +sel |top.centre| +out || +sel |top.mid.legs| +out |feet| +sel |top.mid.number| +out || +sel |top.mid.array| +out || +sel |top.number| +out |9| +sel |top.array| +out |[0,1,{"a":true,"bb":[1,2,false,{"x":"yz"}]}]| +sel |top.array[0]| +out |0| +sel |top.array[1]| +out |1| +sel |top.array[2]| +out |{"a":true,"bb":[1,2,false,{"x":"yz"}]}| +sel |top.array[2].a| +out |true| +sel |top.array[2].b| +out || +sel |top.array[2].bb| +out |[1,2,false,{"x":"yz"}]| +sel |top.array[2].bb[0]| +out |1| +sel |top.array[2].bb[1]| +out |2| +sel |top.array[2].bb[2]| +out |false| +sel |top.array[2].bb[3]| +out |{"x":"yz"}| +sel |top.array[2].bb[3].x| +out |yz| +sel |top.array[2].bb[3].x.nofurther| +out || +sel |top.array[2].bb[4]| +out || +sel |top.array[3]| +out || + diff --git a/usr/src/cmd/dtrace/test/tst/common/json/tst.strsize.d b/usr/src/cmd/dtrace/test/tst/common/json/tst.strsize.d new file mode 100644 index 0000000000..6aa50b9ad0 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/json/tst.strsize.d @@ -0,0 +1,51 @@ +/* + * 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 2012, Joyent, Inc. All rights reserved. + */ + +/* + * ASSERTION: + * json() run time must be bounded above by strsize. This test makes strsize + * small and deliberately overflows it to prove we bail and return NULL in + * the event that we run off the end of the string. + * + */ + +#pragma D option quiet +#pragma D option strsize=18 + +BEGIN +{ + in = "{\"a\": 1024}"; /* length == 19 */ + out = json(in, "a"); + printf("|%s|\n%s\n\n", in, out != NULL ? out : ""); + + in = "{\"a\": 1024}"; /* length == 11 */ + out = json(in, "a"); + printf("|%s|\n%s\n\n", in, out != NULL ? out : ""); + + in = "{\"a\":false,\"b\":true}"; /* length == 20 */ + out = json(in, "b"); + printf("|%s|\n%s\n\n", in, out != NULL ? out : ""); + + in = "{\"a\":false,\"b\":20}"; /* length == 18 */ + out = json(in, "b"); + printf("|%s|\n%s\n\n", in, out != NULL ? out : ""); + + exit(0); +} + +ERROR +{ + exit(1); +} diff --git a/usr/src/cmd/dtrace/test/tst/common/json/tst.strsize.d.out b/usr/src/cmd/dtrace/test/tst/common/json/tst.strsize.d.out new file mode 100644 index 0000000000..7f1d79b6fe --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/json/tst.strsize.d.out @@ -0,0 +1,13 @@ +|{"a": 1024| + + +|{"a": 1024}| +1024 + +|{"a":false,"b":tru| + + +|{"a":false,"b":20}| +20 + + diff --git a/usr/src/cmd/dtrace/test/tst/common/json/tst.usdt.c b/usr/src/cmd/dtrace/test/tst/common/json/tst.usdt.c new file mode 100644 index 0000000000..307106d903 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/json/tst.usdt.c @@ -0,0 +1,61 @@ +/* + * 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 2012 (c), Joyent, Inc. All rights reserved. + */ + +#include +#include "usdt.h" + +#define FMT "{" \ + " \"sizes\": [ \"first\", 2, %f ]," \ + " \"index\": %d," \ + " \"facts\": {" \ + " \"odd\": \"%s\"," \ + " \"even\": \"%s\"" \ + " }," \ + " \"action\": \"%s\"" \ + "}\n" + +int +waiting(volatile int *a) +{ + return (*a); +} + +int +main(int argc, char **argv) +{ + volatile int a = 0; + int idx; + double size = 250.5; + + while (waiting(&a) == 0) + continue; + + for (idx = 0; idx < 10; idx++) { + char *odd, *even, *json, *action; + + size *= 1.78; + odd = idx % 2 == 1 ? "true" : "false"; + even = idx % 2 == 0 ? "true" : "false"; + action = idx == 7 ? "ignore" : "print"; + + asprintf(&json, FMT, size, idx, odd, even, action); + BUNYAN_FAKE_LOG_DEBUG(json); + free(json); + } + + BUNYAN_FAKE_LOG_DEBUG("{\"finished\": true}"); + + return (0); +} diff --git a/usr/src/cmd/dtrace/test/tst/common/json/tst.usdt.d b/usr/src/cmd/dtrace/test/tst/common/json/tst.usdt.d new file mode 100644 index 0000000000..f0fbdd5cab --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/json/tst.usdt.d @@ -0,0 +1,65 @@ +/* + * 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 (c) 2012, Joyent, Inc. All rights reserved. + */ + +#pragma D option strsize=4k +#pragma D option quiet +#pragma D option destructive + +/* + * This test reads a JSON string from a USDT probe, roughly simulating the + * primary motivating use case for the json() subroutine: filtering + * JSON-formatted log messages from a logging subsystem like node-bunyan. + */ + +pid$1:a.out:waiting:entry +{ + this->value = (int *)alloca(sizeof (int)); + *this->value = 1; + copyout(this->value, arg0, sizeof (int)); +} + +bunyan*$1:::log-* +{ + this->j = copyinstr(arg0); +} + +bunyan*$1:::log-* +/json(this->j, "finished") == NULL && json(this->j, "action") != "ignore"/ +{ + this->index = strtoll(json(this->j, "index")); + this->size = json(this->j, "sizes[2]"); + this->odd = json(this->j, "facts.odd"); + this->even = json(this->j, "facts.even"); + printf("[%d] sz %s odd %s even %s\n", this->index, this->size, + this->odd, this->even); +} + +bunyan*$1:::log-* +/json(this->j, "finished") != NULL/ +{ + printf("FINISHED!\n"); + exit(0); +} + +tick-10s +{ + printf("ERROR: Timed out before finish message!\n"); + exit(1); +} + +ERROR +{ + exit(1); +} diff --git a/usr/src/cmd/dtrace/test/tst/common/json/tst.usdt.d.out b/usr/src/cmd/dtrace/test/tst/common/json/tst.usdt.d.out new file mode 100644 index 0000000000..c7f58bb535 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/json/tst.usdt.d.out @@ -0,0 +1,11 @@ +[0] sz 445.890000 odd false even true +[1] sz 793.684200 odd true even false +[2] sz 1412.757876 odd false even true +[3] sz 2514.709019 odd true even false +[4] sz 4476.182054 odd false even true +[5] sz 7967.604057 odd true even false +[6] sz 14182.335221 odd false even true +[8] sz 44935.310914 odd false even true +[9] sz 79984.853427 odd true even false +FINISHED! + diff --git a/usr/src/cmd/dtrace/test/tst/common/json/usdt.d b/usr/src/cmd/dtrace/test/tst/common/json/usdt.d new file mode 100644 index 0000000000..1a4fc87f60 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/json/usdt.d @@ -0,0 +1,27 @@ +/* + * 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 2012, Joyent, Inc. All rights reserved. + */ + +/* + * Sets up a fake node-bunyan-like USDT provider for use from C. + */ + +provider bunyan_fake { + probe log__trace(char *msg); + probe log__debug(char *msg); + probe log__info(char *msg); + probe log__warn(char *msg); + probe log__error(char *msg); + probe log__fatal(char *msg); +}; diff --git a/usr/src/cmd/dtrace/test/tst/common/privs/tst.func_access.ksh b/usr/src/cmd/dtrace/test/tst/common/privs/tst.func_access.ksh index ae4934cdeb..da9bb4c6c6 100644 --- a/usr/src/cmd/dtrace/test/tst/common/privs/tst.func_access.ksh +++ b/usr/src/cmd/dtrace/test/tst/common/privs/tst.func_access.ksh @@ -22,8 +22,8 @@ # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright (c) 2012, Joyent, Inc. All rights reserved. # -#ident "%Z%%M% %I% %E% SMI" ppriv -s A=basic,dtrace_proc,dtrace_user $$ @@ -31,7 +31,7 @@ ppriv -s A=basic,dtrace_proc,dtrace_user $$ BEGIN { errorcount = 0; - expected_errorcount = 23; + expected_errorcount = 27; } BEGIN { trace(mutex_owned(&`pidlock)); } @@ -55,6 +55,8 @@ BEGIN { trace(strtok(`initname, "/")); } BEGIN { trace(strtok(NULL, "/")); } BEGIN { trace(strtok("foo/bar", `initname)); } BEGIN { trace(strtok(NULL, `initname)); } +BEGIN { trace(strtoll(`initname)); } +BEGIN { trace(strtoll(`initname, 10)); } BEGIN { trace(substr(`initname, 2, 3)); } BEGIN { trace(ddi_pathname(`top_devinfo, 1)); } @@ -63,6 +65,9 @@ BEGIN { trace(strjoin("foo", `initname)); } BEGIN { trace(dirname(`initname)); } BEGIN { trace(cleanpath(`initname)); } +BEGIN { j = "{\"/sbin/init\":\"uh oh\"}"; trace(json(j, `initname)); } +BEGIN { trace(json(`initname, "x")); } + ERROR { errorcount++; } diff --git a/usr/src/cmd/dtrace/test/tst/common/strtoll/err.BaseTooLarge.d b/usr/src/cmd/dtrace/test/tst/common/strtoll/err.BaseTooLarge.d new file mode 100644 index 0000000000..4d6b5af75b --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/strtoll/err.BaseTooLarge.d @@ -0,0 +1,35 @@ +/* + * 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 (c) 2012, Joyent, Inc. All rights reserved. + */ + +/* + * ASSERTION: + * The largest base we will accept is Base 36 -- i.e. using all of 0-9 and + * A-Z as numerals. + * + * SECTION: Actions and Subroutines/strtoll() + */ + +#pragma D option quiet + +BEGIN +{ + printf("%d\n", strtoll("0", 37)); + exit(0); +} + +ERROR +{ + exit(1); +} diff --git a/usr/src/cmd/dtrace/test/tst/common/strtoll/err.BaseTooSmall.d b/usr/src/cmd/dtrace/test/tst/common/strtoll/err.BaseTooSmall.d new file mode 100644 index 0000000000..de56b50e9e --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/strtoll/err.BaseTooSmall.d @@ -0,0 +1,34 @@ +/* + * 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 (c) 2012, Joyent, Inc. All rights reserved. + */ + +/* + * ASSERTION: + * The smallest base we will accept is Base 2. + * + * SECTION: Actions and Subroutines/strtoll() + */ + +#pragma D option quiet + +BEGIN +{ + printf("%d\n", strtoll("0", 1)); + exit(0); +} + +ERROR +{ + exit(1); +} diff --git a/usr/src/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d b/usr/src/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d new file mode 100644 index 0000000000..0b1812a53c --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d @@ -0,0 +1,66 @@ +/* + * 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 (c) 2012, Joyent, Inc. All rights reserved. + */ + +/* + * ASSERTION: + * Test the strtoll() subroutine. + * + * SECTION: Actions and Subroutines/strtoll() + */ + +#pragma D option quiet + +BEGIN +{ + + /* minimum base (2) and maximum base (36): */ + printf("%d\n", strtoll("0", 2)); + printf("%d\n", strtoll("1", 36)); + + /* simple tests: */ + printf("%d\n", strtoll("0x20", 16)); + printf("%d\n", strtoll("-32", 10)); + printf("%d\n", strtoll("010", 8)); + printf("%d\n", strtoll("101010", 2)); + + /* INT64_MIN and INT64_MAX: */ + printf("%d\n", strtoll("9223372036854775807")); + printf("%d\n", strtoll("-9223372036854775808")); + printf("%d\n", strtoll("0777777777777777777777", 8)); + printf("%d\n", strtoll("-01000000000000000000000", 8)); + + /* wrapping: */ + printf("%d\n", strtoll("1000000000000000000000", 8)); + printf("%d\n", strtoll("-1000000000000000000001", 8)); + + /* hex without prefix: */ + printf("%d\n", strtoll("baddcafe", 16)); + + /* stopping at first out-of-base character: */ + printf("%d\n", strtoll("12j", 10)); + printf("%d\n", strtoll("102", 2)); + + /* base 36: */ + printf("%d\n", strtoll("-0DTrace4EverZ", 36)); + + /* base 10 is assumed: */ + printf("%d\n", strtoll("1985")); + printf("%d\n", strtoll("-2012")); + + /* empty string: */ + printf("%d\n", strtoll("")); + + exit(0); +} diff --git a/usr/src/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d.out b/usr/src/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d.out new file mode 100644 index 0000000000..d12eb9c197 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d.out @@ -0,0 +1,20 @@ +0 +1 +32 +-32 +8 +42 +9223372036854775807 +-9223372036854775808 +9223372036854775807 +-9223372036854775808 +-9223372036854775808 +9223372036854775807 +3135097598 +12 +2 +-1819882045752187535 +1985 +-2012 +0 + diff --git a/usr/src/common/util/strtolctype.h b/usr/src/common/util/strtolctype.h index 7b7afc6e30..5675e42be7 100644 --- a/usr/src/common/util/strtolctype.h +++ b/usr/src/common/util/strtolctype.h @@ -38,6 +38,10 @@ extern "C" { * This header file contains a collection of macros that the strtou?ll? * functions in common/util use to test characters. What we need is a kernel * version of ctype.h. + * + * NOTE: These macros are used within several DTrace probe context functions. + * They must not be altered to make function calls or perform actions not + * safe in probe context. */ #if defined(_KERNEL) && !defined(_BOOT) diff --git a/usr/src/lib/libdtrace/common/dt_open.c b/usr/src/lib/libdtrace/common/dt_open.c index 2c6640e82f..f9c235df44 100644 --- a/usr/src/lib/libdtrace/common/dt_open.c +++ b/usr/src/lib/libdtrace/common/dt_open.c @@ -112,8 +112,9 @@ #define DT_VERS_1_9 DT_VERSION_NUMBER(1, 9, 0) #define DT_VERS_1_9_1 DT_VERSION_NUMBER(1, 9, 1) #define DT_VERS_1_10 DT_VERSION_NUMBER(1, 10, 0) -#define DT_VERS_LATEST DT_VERS_1_10 -#define DT_VERS_STRING "Sun D 1.10" +#define DT_VERS_1_11 DT_VERSION_NUMBER(1, 11, 0) +#define DT_VERS_LATEST DT_VERS_1_11 +#define DT_VERS_STRING "Sun D 1.11" const dt_version_t _dtrace_versions[] = { DT_VERS_1_0, /* D API 1.0.0 (PSARC 2001/466) Solaris 10 FCS */ @@ -136,6 +137,7 @@ const dt_version_t _dtrace_versions[] = { DT_VERS_1_9, /* D API 1.9 */ DT_VERS_1_9_1, /* D API 1.9.1 */ DT_VERS_1_10, /* D API 1.10 */ + DT_VERS_1_11, /* D API 1.11 */ 0 }; @@ -265,6 +267,8 @@ static const dt_ident_t _dtrace_globals[] = { DT_VERS_1_5, &dt_idops_func, "string(int, void *)" }, { "ipl", DT_IDENT_SCALAR, 0, DIF_VAR_IPL, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint_t" }, +{ "json", DT_IDENT_FUNC, 0, DIF_SUBR_JSON, DT_ATTR_STABCMN, DT_VERS_1_11, + &dt_idops_func, "string(const char *, const char *)" }, { "jstack", DT_IDENT_ACTFUNC, 0, DT_ACT_JSTACK, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "stack(...)" }, { "lltostr", DT_IDENT_FUNC, 0, DIF_SUBR_LLTOSTR, DT_ATTR_STABCMN, DT_VERS_1_0, @@ -379,6 +383,8 @@ static const dt_ident_t _dtrace_globals[] = { &dt_idops_func, "string(const char *, const char *)" }, { "strtok", DT_IDENT_FUNC, 0, DIF_SUBR_STRTOK, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, const char *)" }, +{ "strtoll", DT_IDENT_FUNC, 0, DIF_SUBR_STRTOLL, DT_ATTR_STABCMN, DT_VERS_1_11, + &dt_idops_func, "int64_t(const char *, [int])" }, { "substr", DT_IDENT_FUNC, 0, DIF_SUBR_SUBSTR, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, int, [int])" }, { "sum", DT_IDENT_AGGFUNC, 0, DTRACEAGG_SUM, DT_ATTR_STABCMN, DT_VERS_1_0, diff --git a/usr/src/pkg/manifests/system-dtrace-tests.mf b/usr/src/pkg/manifests/system-dtrace-tests.mf index fdab627515..a71654ab26 100644 --- a/usr/src/pkg/manifests/system-dtrace-tests.mf +++ b/usr/src/pkg/manifests/system-dtrace-tests.mf @@ -71,6 +71,7 @@ dir path=opt/SUNWdtrt/tst/common/inline dir path=opt/SUNWdtrt/tst/common/io dir path=opt/SUNWdtrt/tst/common/ip dir path=opt/SUNWdtrt/tst/common/java_api +dir path=opt/SUNWdtrt/tst/common/json dir path=opt/SUNWdtrt/tst/common/lexer dir path=opt/SUNWdtrt/tst/common/llquantize dir path=opt/SUNWdtrt/tst/common/mdb @@ -108,6 +109,7 @@ dir path=opt/SUNWdtrt/tst/common/stack dir path=opt/SUNWdtrt/tst/common/stackdepth dir path=opt/SUNWdtrt/tst/common/stop dir path=opt/SUNWdtrt/tst/common/strlen +dir path=opt/SUNWdtrt/tst/common/strtoll dir path=opt/SUNWdtrt/tst/common/struct dir path=opt/SUNWdtrt/tst/common/syscall dir path=opt/SUNWdtrt/tst/common/sysevent @@ -957,6 +959,13 @@ file path=opt/SUNWdtrt/tst/common/java_api/tst.StopLock.ksh mode=0444 file path=opt/SUNWdtrt/tst/common/java_api/tst.StopLock.ksh.out mode=0444 file path=opt/SUNWdtrt/tst/common/java_api/tst.printa.d mode=0444 file path=opt/SUNWdtrt/tst/common/java_api/tst.printa.d.out mode=0444 +file path=opt/SUNWdtrt/tst/common/json/tst.general.d mode=0444 +file path=opt/SUNWdtrt/tst/common/json/tst.general.d.out mode=0444 +file path=opt/SUNWdtrt/tst/common/json/tst.strsize.d mode=0444 +file path=opt/SUNWdtrt/tst/common/json/tst.strsize.d.out mode=0444 +file path=opt/SUNWdtrt/tst/common/json/tst.usdt.d mode=0444 +file path=opt/SUNWdtrt/tst/common/json/tst.usdt.d.out mode=0444 +file path=opt/SUNWdtrt/tst/common/json/tst.usdt.exe mode=0555 file path=opt/SUNWdtrt/tst/common/lexer/err.D_CHR_NL.char.d mode=0444 file path=opt/SUNWdtrt/tst/common/lexer/err.D_CHR_NULL.char.d mode=0444 file path=opt/SUNWdtrt/tst/common/lexer/err.D_INT_DIGIT.InvalidDigit.d \ @@ -1757,6 +1766,10 @@ file path=opt/SUNWdtrt/tst/common/stop/tst.stop1.exe mode=0555 file path=opt/SUNWdtrt/tst/common/stop/tst.stop2.d mode=0444 file path=opt/SUNWdtrt/tst/common/stop/tst.stop2.exe mode=0555 file path=opt/SUNWdtrt/tst/common/strlen/tst.strlen1.d mode=0444 +file path=opt/SUNWdtrt/tst/common/strtoll/err.BaseTooLarge.d mode=0444 +file path=opt/SUNWdtrt/tst/common/strtoll/err.BaseTooSmall.d mode=0444 +file path=opt/SUNWdtrt/tst/common/strtoll/tst.strtoll.d mode=0444 +file path=opt/SUNWdtrt/tst/common/strtoll/tst.strtoll.d.out mode=0444 file path=opt/SUNWdtrt/tst/common/struct/err.D_ADDROF_VAR.StructPointer.d \ mode=0444 file path=opt/SUNWdtrt/tst/common/struct/err.D_DECL_COMBO.StructWithoutColon.d \ diff --git a/usr/src/uts/common/dtrace/dtrace.c b/usr/src/uts/common/dtrace/dtrace.c index d4d3d9c86e..3363a0dc55 100644 --- a/usr/src/uts/common/dtrace/dtrace.c +++ b/usr/src/uts/common/dtrace/dtrace.c @@ -92,6 +92,7 @@ #include #include #include +#include "strtolctype.h" /* * DTrace Tunable Variables @@ -859,6 +860,58 @@ dtrace_vcanload(void *src, dtrace_diftype_t *type, dtrace_mstate_t *mstate, } /* + * Convert a string to a signed integer using safe loads. + * + * NOTE: This function uses various macros from strtolctype.h to manipulate + * digit values, etc -- these have all been checked to ensure they make + * no additional function calls. + */ +static int64_t +dtrace_strtoll(char *input, int base, size_t limit) +{ + uintptr_t pos = (uintptr_t)input; + int64_t val = 0; + int x; + boolean_t neg = B_FALSE; + char c, cc, ccc; + uintptr_t end = pos + limit; + + /* + * Consume any whitespace preceding digits. + */ + while ((c = dtrace_load8(pos)) == ' ' || c == '\t') + pos++; + + /* + * Handle an explicit sign if one is present. + */ + if (c == '-' || c == '+') { + if (c == '-') + neg = B_TRUE; + c = dtrace_load8(++pos); + } + + /* + * Check for an explicit hexadecimal prefix ("0x" or "0X") and skip it + * if present. + */ + if (base == 16 && c == '0' && ((cc = dtrace_load8(pos + 1)) == 'x' || + cc == 'X') && isxdigit(ccc = dtrace_load8(pos + 2))) { + pos += 2; + c = ccc; + } + + /* + * Read in contiguous digits until the first non-digit character. + */ + for (; pos < end && c != '\0' && lisalnum(c) && (x = DIGIT(c)) < base; + c = dtrace_load8(++pos)) + val = val * base + x; + + return (neg ? -val : val); +} + +/* * Compare two strings using safe loads. */ static int @@ -3335,6 +3388,463 @@ dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v, } } + +typedef enum dtrace_json_state { + DTRACE_JSON_REST = 1, + DTRACE_JSON_OBJECT, + DTRACE_JSON_STRING, + DTRACE_JSON_STRING_ESCAPE, + DTRACE_JSON_STRING_ESCAPE_UNICODE, + DTRACE_JSON_COLON, + DTRACE_JSON_COMMA, + DTRACE_JSON_VALUE, + DTRACE_JSON_IDENTIFIER, + DTRACE_JSON_NUMBER, + DTRACE_JSON_NUMBER_FRAC, + DTRACE_JSON_NUMBER_EXP, + DTRACE_JSON_COLLECT_OBJECT +} dtrace_json_state_t; + +/* + * This function possesses just enough knowledge about JSON to extract a single + * value from a JSON string and store it in the scratch buffer. It is able + * to extract nested object values, and members of arrays by index. + * + * elemlist is a list of JSON keys, stored as packed NUL-terminated strings, to + * be looked up as we descend into the object tree. e.g. + * + * foo[0].bar.baz[32] --> "foo" NUL "0" NUL "bar" NUL "baz" NUL "32" NUL + * with nelems = 5. + * + * The run time of this function must be bounded above by strsize to limit the + * amount of work done in probe context. As such, it is implemented as a + * simple state machine, reading one character at a time using safe loads + * until we find the requested element, hit a parsing error or run off the + * end of the object or string. + * + * As there is no way for a subroutine to return an error without interrupting + * clause execution, we simply return NULL in the event of a missing key or any + * other error condition. Each NULL return in this function is commented with + * the error condition it represents -- parsing or otherwise. + * + * The set of states for the state machine closely matches the JSON + * specification (http://json.org/). Briefly: + * + * DTRACE_JSON_REST: + * Skip whitespace until we find either a top-level Object, moving + * to DTRACE_JSON_OBJECT; or an Array, moving to DTRACE_JSON_VALUE. + * + * DTRACE_JSON_OBJECT: + * Locate the next key String in an Object. Sets a flag to denote + * the next String as a key string and moves to DTRACE_JSON_STRING. + * + * DTRACE_JSON_COLON: + * Skip whitespace until we find the colon that separates key Strings + * from their values. Once found, move to DTRACE_JSON_VALUE. + * + * DTRACE_JSON_VALUE: + * Detects the type of the next value (String, Number, Identifier, Object + * or Array) and routes to the states that process that type. Here we also + * deal with the element selector list if we are requested to traverse down + * into the object tree. + * + * DTRACE_JSON_COMMA: + * Skip whitespace until we find the comma that separates key-value pairs + * in Objects (returning to DTRACE_JSON_OBJECT) or values in Arrays + * (similarly DTRACE_JSON_VALUE). All following literal value processing + * states return to this state at the end of their value, unless otherwise + * noted. + * + * DTRACE_JSON_NUMBER, DTRACE_JSON_NUMBER_FRAC, DTRACE_JSON_NUMBER_EXP: + * Processes a Number literal from the JSON, including any exponent + * component that may be present. Numbers are returned as strings, which + * may be passed to strtoll() if an integer is required. + * + * DTRACE_JSON_IDENTIFIER: + * Processes a "true", "false" or "null" literal in the JSON. + * + * DTRACE_JSON_STRING, DTRACE_JSON_STRING_ESCAPE, + * DTRACE_JSON_STRING_ESCAPE_UNICODE: + * Processes a String literal from the JSON, whether the String denotes + * a key, a value or part of a larger Object. Handles all escape sequences + * present in the specification, including four-digit unicode characters, + * but merely includes the escape sequence without converting it to the + * actual escaped character. If the String is flagged as a key, we + * move to DTRACE_JSON_COLON rather than DTRACE_JSON_COMMA. + * + * DTRACE_JSON_COLLECT_OBJECT: + * This state collects an entire Object (or Array), correctly handling + * embedded strings. If the full element selector list matches this nested + * object, we return the Object in full as a string. If not, we use this + * state to skip to the next value at this level and continue processing. + * + * NOTE: This function uses various macros from strtolctype.h to manipulate + * digit values, etc -- these have all been checked to ensure they make + * no additional function calls. + */ +static char * +dtrace_json(uint64_t size, uintptr_t json, char *elemlist, int nelems, + char *dest) +{ + dtrace_json_state_t state = DTRACE_JSON_REST; + int64_t array_elem = INT64_MIN; + int64_t array_pos = 0; + uint8_t escape_unicount = 0; + boolean_t string_is_key = B_FALSE; + boolean_t collect_object = B_FALSE; + boolean_t found_key = B_FALSE; + boolean_t in_array = B_FALSE; + uint32_t braces = 0, brackets = 0; + char *elem = elemlist; + char *dd = dest; + uintptr_t cur; + + for (cur = json; cur < json + size; cur++) { + char cc = dtrace_load8(cur); + if (cc == '\0') + return (NULL); + + switch (state) { + case DTRACE_JSON_REST: + if (isspace(cc)) + break; + + if (cc == '{') { + state = DTRACE_JSON_OBJECT; + break; + } + + if (cc == '[') { + in_array = B_TRUE; + array_pos = 0; + array_elem = dtrace_strtoll(elem, 10, size); + found_key = array_elem == 0 ? B_TRUE : B_FALSE; + state = DTRACE_JSON_VALUE; + break; + } + + /* + * ERROR: expected to find a top-level object or array. + */ + return (NULL); + case DTRACE_JSON_OBJECT: + if (isspace(cc)) + break; + + if (cc == '"') { + state = DTRACE_JSON_STRING; + string_is_key = B_TRUE; + break; + } + + /* + * ERROR: either the object did not start with a key + * string, or we've run off the end of the object + * without finding the requested key. + */ + return (NULL); + case DTRACE_JSON_STRING: + if (cc == '\\') { + *dd++ = '\\'; + state = DTRACE_JSON_STRING_ESCAPE; + break; + } + + if (cc == '"') { + if (collect_object) { + /* + * We don't reset the dest here, as + * the string is part of a larger + * object being collected. + */ + *dd++ = cc; + collect_object = B_FALSE; + state = DTRACE_JSON_COLLECT_OBJECT; + break; + } + *dd = '\0'; + dd = dest; /* reset string buffer */ + if (string_is_key) { + if (dtrace_strncmp(dest, elem, + size) == 0) + found_key = B_TRUE; + } else if (found_key) { + if (nelems > 1) { + /* + * We expected an object, not + * this string. + */ + return (NULL); + } + return (dest); + } + state = string_is_key ? DTRACE_JSON_COLON : + DTRACE_JSON_COMMA; + string_is_key = B_FALSE; + break; + } + + *dd++ = cc; + break; + case DTRACE_JSON_STRING_ESCAPE: + *dd++ = cc; + if (cc == 'u') { + escape_unicount = 0; + state = DTRACE_JSON_STRING_ESCAPE_UNICODE; + } else { + state = DTRACE_JSON_STRING; + } + break; + case DTRACE_JSON_STRING_ESCAPE_UNICODE: + if (!isxdigit(cc)) { + /* + * ERROR: invalid unicode escape, expected + * four valid hexidecimal digits. + */ + return (NULL); + } + + *dd++ = cc; + if (++escape_unicount == 4) + state = DTRACE_JSON_STRING; + break; + case DTRACE_JSON_COLON: + if (isspace(cc)) + break; + + if (cc == ':') { + state = DTRACE_JSON_VALUE; + break; + } + + /* + * ERROR: expected a colon. + */ + return (NULL); + case DTRACE_JSON_COMMA: + if (isspace(cc)) + break; + + if (cc == ',') { + if (in_array) { + state = DTRACE_JSON_VALUE; + if (++array_pos == array_elem) + found_key = B_TRUE; + } else { + state = DTRACE_JSON_OBJECT; + } + break; + } + + /* + * ERROR: either we hit an unexpected character, or + * we reached the end of the object or array without + * finding the requested key. + */ + return (NULL); + case DTRACE_JSON_IDENTIFIER: + if (islower(cc)) { + *dd++ = cc; + break; + } + + *dd = '\0'; + dd = dest; /* reset string buffer */ + + if (dtrace_strncmp(dest, "true", 5) == 0 || + dtrace_strncmp(dest, "false", 6) == 0 || + dtrace_strncmp(dest, "null", 5) == 0) { + if (found_key) { + if (nelems > 1) { + /* + * ERROR: We expected an object, + * not this identifier. + */ + return (NULL); + } + return (dest); + } else { + cur--; + state = DTRACE_JSON_COMMA; + break; + } + } + + /* + * ERROR: we did not recognise the identifier as one + * of those in the JSON specification. + */ + return (NULL); + case DTRACE_JSON_NUMBER: + if (cc == '.') { + *dd++ = cc; + state = DTRACE_JSON_NUMBER_FRAC; + break; + } + + if (cc == 'x' || cc == 'X') { + /* + * ERROR: specification explicitly excludes + * hexidecimal or octal numbers. + */ + return (NULL); + } + + /* FALLTHRU */ + case DTRACE_JSON_NUMBER_FRAC: + if (cc == 'e' || cc == 'E') { + *dd++ = cc; + state = DTRACE_JSON_NUMBER_EXP; + break; + } + + if (cc == '+' || cc == '-') { + /* + * ERROR: expect sign as part of exponent only. + */ + return (NULL); + } + /* FALLTHRU */ + case DTRACE_JSON_NUMBER_EXP: + if (isdigit(cc) || cc == '+' || cc == '-') { + *dd++ = cc; + break; + } + + *dd = '\0'; + dd = dest; /* reset string buffer */ + if (found_key) { + if (nelems > 1) { + /* + * ERROR: We expected an object, not + * this number. + */ + return (NULL); + } + return (dest); + } + + cur--; + state = DTRACE_JSON_COMMA; + break; + case DTRACE_JSON_VALUE: + if (isspace(cc)) + break; + + if (cc == '{' || cc == '[') { + if (nelems > 1 && found_key) { + in_array = cc == '[' ? B_TRUE : B_FALSE; + /* + * If our element selector directs us + * to descend into this nested object, + * then move to the next selector + * element in the list and restart the + * state machine. + */ + while (*elem != '\0') + elem++; + elem++; /* skip the inter-element NUL */ + nelems--; + dd = dest; + if (in_array) { + state = DTRACE_JSON_VALUE; + array_pos = 0; + array_elem = dtrace_strtoll( + elem, 10, size); + found_key = array_elem == 0 ? + B_TRUE : B_FALSE; + } else { + found_key = B_FALSE; + state = DTRACE_JSON_OBJECT; + } + break; + } + + /* + * Otherwise, we wish to either skip this + * nested object or return it in full. + */ + if (cc == '[') + brackets = 1; + else + braces = 1; + *dd++ = cc; + state = DTRACE_JSON_COLLECT_OBJECT; + break; + } + + if (cc == '"') { + state = DTRACE_JSON_STRING; + break; + } + + if (islower(cc)) { + /* + * Here we deal with true, false and null. + */ + *dd++ = cc; + state = DTRACE_JSON_IDENTIFIER; + break; + } + + if (cc == '-' || isdigit(cc)) { + *dd++ = cc; + state = DTRACE_JSON_NUMBER; + break; + } + + /* + * ERROR: unexpected character at start of value. + */ + return (NULL); + case DTRACE_JSON_COLLECT_OBJECT: + if (cc == '\0') + /* + * ERROR: unexpected end of input. + */ + return (NULL); + + *dd++ = cc; + if (cc == '"') { + collect_object = B_TRUE; + state = DTRACE_JSON_STRING; + break; + } + + if (cc == ']') { + if (brackets-- == 0) { + /* + * ERROR: unbalanced brackets. + */ + return (NULL); + } + } else if (cc == '}') { + if (braces-- == 0) { + /* + * ERROR: unbalanced braces. + */ + return (NULL); + } + } else if (cc == '{') { + braces++; + } else if (cc == '[') { + brackets++; + } + + if (brackets == 0 && braces == 0) { + if (found_key) { + *dd = '\0'; + return (dest); + } + dd = dest; /* reset string buffer */ + state = DTRACE_JSON_COMMA; + } + break; + } + } + return (NULL); +} + /* * Emulate the execution of DTrace ID subroutines invoked by the call opcode. * Notice that we don't bother validating the proper number of arguments or @@ -4035,6 +4545,65 @@ dtrace_dif_subr(uint_t subr, uint_t rd, uint64_t *regs, break; } + case DIF_SUBR_JSON: { + uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; + uintptr_t json = tupregs[0].dttk_value; + size_t jsonlen = dtrace_strlen((char *)json, size); + uintptr_t elem = tupregs[1].dttk_value; + size_t elemlen = dtrace_strlen((char *)elem, size); + + char *dest = (char *)mstate->dtms_scratch_ptr; + char *elemlist = (char *)mstate->dtms_scratch_ptr + jsonlen + 1; + char *ee = elemlist; + int nelems = 1; + uintptr_t cur; + + if (!dtrace_canload(json, jsonlen + 1, mstate, vstate) || + !dtrace_canload(elem, elemlen + 1, mstate, vstate)) { + regs[rd] = NULL; + break; + } + + if (!DTRACE_INSCRATCH(mstate, jsonlen + 1 + elemlen + 1)) { + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); + regs[rd] = NULL; + break; + } + + /* + * Read the element selector and split it up into a packed list + * of strings. + */ + for (cur = elem; cur < elem + elemlen; cur++) { + char cc = dtrace_load8(cur); + + if (cur == elem && cc == '[') { + /* + * If the first element selector key is + * actually an array index then ignore the + * bracket. + */ + continue; + } + + if (cc == ']') + continue; + + if (cc == '.' || cc == '[') { + nelems++; + cc = '\0'; + } + + *ee++ = cc; + } + *ee++ = '\0'; + + if ((regs[rd] = (uintptr_t)dtrace_json(size, json, elemlist, + nelems, dest)) != NULL) + mstate->dtms_scratch_ptr += jsonlen + 1; + break; + } + case DIF_SUBR_TOUPPER: case DIF_SUBR_TOLOWER: { uintptr_t s = tupregs[0].dttk_value; @@ -4342,6 +4911,28 @@ case DIF_SUBR_GETMAJOR: break; } + case DIF_SUBR_STRTOLL: { + uintptr_t s = tupregs[0].dttk_value; + uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; + int base = 10; + + if (nargs > 1) { + if ((base = tupregs[1].dttk_value) <= 1 || + base > ('z' - 'a' + 1) + ('9' - '0' + 1)) { + *flags |= CPU_DTRACE_ILLOP; + break; + } + } + + if (!dtrace_strcanload(s, size, mstate, vstate)) { + regs[rd] = INT64_MIN; + break; + } + + regs[rd] = dtrace_strtoll((char *)s, base, size); + break; + } + case DIF_SUBR_LLTOSTR: { int64_t i = (int64_t)tupregs[0].dttk_value; uint64_t val, digit; @@ -8861,7 +9452,9 @@ dtrace_difo_validate_helper(dtrace_difo_t *dp) subr == DIF_SUBR_INET_NTOA || subr == DIF_SUBR_INET_NTOA6 || subr == DIF_SUBR_INET_NTOP || + subr == DIF_SUBR_JSON || subr == DIF_SUBR_LLTOSTR || + subr == DIF_SUBR_STRTOLL || subr == DIF_SUBR_RINDEX || subr == DIF_SUBR_STRCHR || subr == DIF_SUBR_STRJOIN || diff --git a/usr/src/uts/common/sys/dtrace.h b/usr/src/uts/common/sys/dtrace.h index 5da41b409b..005bd62ebd 100644 --- a/usr/src/uts/common/sys/dtrace.h +++ b/usr/src/uts/common/sys/dtrace.h @@ -289,8 +289,10 @@ typedef enum dtrace_probespec { #define DIF_SUBR_TOUPPER 44 #define DIF_SUBR_TOLOWER 45 #define DIF_SUBR_GETF 46 +#define DIF_SUBR_JSON 47 +#define DIF_SUBR_STRTOLL 48 -#define DIF_SUBR_MAX 46 /* max subroutine value */ +#define DIF_SUBR_MAX 48 /* max subroutine value */ typedef uint32_t dif_instr_t; diff --git a/usr/src/uts/intel/dtrace/Makefile b/usr/src/uts/intel/dtrace/Makefile index ba914bcbaa..aeabf4a7e3 100644 --- a/usr/src/uts/intel/dtrace/Makefile +++ b/usr/src/uts/intel/dtrace/Makefile @@ -49,6 +49,8 @@ CERRWARN += -_gcc=-Wno-parentheses CERRWARN += -_gcc=-Wno-type-limits CERRWARN += -_gcc=-Wno-uninitialized +CPPFLAGS += -I$(SRC)/common/util + ALL_TARGET = $(BINARY) $(SRC_CONFILE) LINT_TARGET = $(MODULE).lint INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) diff --git a/usr/src/uts/sparc/dtrace/Makefile b/usr/src/uts/sparc/dtrace/Makefile index 3a2d5341b1..7fbedc8cbe 100644 --- a/usr/src/uts/sparc/dtrace/Makefile +++ b/usr/src/uts/sparc/dtrace/Makefile @@ -52,6 +52,7 @@ DTRACE_INC_64 = -I$(UTSBASE)/sparc/v9 CFLAGS += $(CCVERBOSE) CPPFLAGS += $(DTRACE_INC_$(CLASS)) +CPPFLAGS += -I$(SRC)/common/util DTRACE_XAS_32 = -xarch=v8plus DTRACE_XAS_64 = -xarch=v9 -- 2.11.4.GIT