MFV illumos

4477 DTrace should speak JSON

MFC after:	2 weeks
This commit is contained in:
rpaulo 2014-06-26 21:45:49 +00:00
commit c6d679e670
19 changed files with 1477 additions and 6 deletions

View File

@ -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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
@ -98,6 +99,8 @@ STRFUNC(inet_ntoa((ipaddr_t *)alloca(sizeof (ipaddr_t))))
STRFUNC(inet_ntoa6((in6_addr_t *)alloca(sizeof (in6_addr_t))))
STRFUNC(inet_ntop(AF_INET, (void *)alloca(sizeof (ipaddr_t))))
INTFUNC(getf(0))
INTFUNC(strtoll("0x12EE5D5", 16))
STRFUNC(json("{\"systemtap\": false}", "systemtap"))
BEGIN
/subr == DIF_SUBR_MAX + 1/

View File

@ -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 : "<NULL>")
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);
}

View File

@ -0,0 +1,218 @@
tst |empty array|
in |[]|
sel |0|
out |<NULL>|
tst |one-element array: integer|
in |[1]|
sel |0|
out |1|
sel |1|
out |<NULL>|
sel |100|
out |<NULL>|
sel |-1|
out |<NULL>|
tst |one-element array: hex integer (not in spec, not supported)|
in |[0x1000]|
sel |0|
out |<NULL>|
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 |<NULL>|
tst |one-element array: object|
in |[ { "first": true, "second": false }]|
sel |0.first|
out |true|
sel |0.second|
out |false|
sel |0.third|
out |<NULL>|
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 |<NULL>|
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 |<NULL>|
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 |<NULL>|
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 |<NULL>|
sel |7.nonexist|
out |<NULL>|
sel |8|
out |null|
sel |9|
out |<NULL>|
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 |<NULL>|
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 |<NULL>|
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 |<NULL>|
sel |7.nonexist|
out |<NULL>|
sel |8|
out |null|
sel |9|
out |<NULL>|
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 |<NULL>|
sel |2|
out |<NULL>|
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 |<NULL>|
sel |top.mid|
out |{ "legs": "feet" }|
sel |top.centre|
out |<NULL>|
sel |top.mid.legs|
out |feet|
sel |top.mid.number|
out |<NULL>|
sel |top.mid.array|
out |<NULL>|
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 |<NULL>|
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 |<NULL>|
sel |top.array[2].bb[4]|
out |<NULL>|
sel |top.array[3]|
out |<NULL>|

View File

@ -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 : "<NULL>");
in = "{\"a\": 1024}"; /* length == 11 */
out = json(in, "a");
printf("|%s|\n%s\n\n", in, out != NULL ? out : "<NULL>");
in = "{\"a\":false,\"b\":true}"; /* length == 20 */
out = json(in, "b");
printf("|%s|\n%s\n\n", in, out != NULL ? out : "<NULL>");
in = "{\"a\":false,\"b\":20}"; /* length == 18 */
out = json(in, "b");
printf("|%s|\n%s\n\n", in, out != NULL ? out : "<NULL>");
exit(0);
}
ERROR
{
exit(1);
}

View File

@ -0,0 +1,13 @@
|{"a": 1024|
<NULL>
|{"a": 1024}|
1024
|{"a":false,"b":tru|
<NULL>
|{"a":false,"b":20}|
20

View File

@ -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 <sys/sdt.h>
#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);
}

View File

@ -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);
}

View File

@ -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!

View File

@ -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);
};

View File

@ -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++;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -0,0 +1,20 @@
0
1
32
-32
8
42
9223372036854775807
-9223372036854775808
9223372036854775807
-9223372036854775808
-9223372036854775808
9223372036854775807
3135097598
12
2
-1819882045752187535
1985
-2012
0

View File

@ -0,0 +1,79 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
#ifndef _COMMON_UTIL_CTYPE_H
#define _COMMON_UTIL_CTYPE_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* 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(sun) && (defined(_KERNEL) || defined(_BOOT))
#define isalnum(ch) (isalpha(ch) || isdigit(ch))
#define isalpha(ch) (isupper(ch) || islower(ch))
#define isdigit(ch) ((ch) >= '0' && (ch) <= '9')
#define islower(ch) ((ch) >= 'a' && (ch) <= 'z')
#define isspace(ch) (((ch) == ' ') || ((ch) == '\r') || ((ch) == '\n') || \
((ch) == '\t') || ((ch) == '\f'))
#define isupper(ch) ((ch) >= 'A' && (ch) <= 'Z')
#define isxdigit(ch) (isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \
((ch) >= 'A' && (ch) <= 'F'))
#endif /* _KERNEL || _BOOT */
#define DIGIT(x) \
(isdigit(x) ? (x) - '0' : islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A')
#define MBASE ('z' - 'a' + 1 + 10)
/*
* The following macro is a version of isalnum() that limits alphabetic
* characters to the ranges a-z and A-Z; locale dependent characters will not
* return 1. The members of a-z and A-Z are assumed to be in ascending order
* and contiguous.
*/
#define lisalnum(x) \
(isdigit(x) || ((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z'))
#ifdef __cplusplus
}
#endif
#endif /* _COMMON_UTIL_CTYPE_H */

View File

@ -123,8 +123,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 */
@ -147,6 +148,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
};
@ -301,6 +303,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,
@ -452,6 +456,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,

View File

@ -111,6 +111,7 @@
#include <sys/zone.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "strtolctype.h"
/* FreeBSD includes: */
#if !defined(sun)
@ -966,6 +967,58 @@ dtrace_vcanload(void *src, dtrace_diftype_t *type, dtrace_mstate_t *mstate,
return (dtrace_canload((uintptr_t)src, sz, mstate, vstate));
}
/*
* 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.
*/
@ -3469,6 +3522,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
@ -4265,6 +4775,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] = 0;
break;
}
if (!DTRACE_INSCRATCH(mstate, jsonlen + 1 + elemlen + 1)) {
DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
regs[rd] = 0;
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)) != 0)
mstate->dtms_scratch_ptr += jsonlen + 1;
break;
}
case DIF_SUBR_TOUPPER:
case DIF_SUBR_TOLOWER: {
uintptr_t s = tupregs[0].dttk_value;
@ -4574,6 +5143,28 @@ dtrace_dif_subr(uint_t subr, uint_t rd, uint64_t *regs,
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;
@ -9373,7 +9964,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 ||

View File

@ -313,7 +313,9 @@ typedef enum dtrace_probespec {
#define DIF_SUBR_SX_ISEXCLUSIVE 50
#define DIF_SUBR_MEMSTR 51
#define DIF_SUBR_GETF 52
#define DIF_SUBR_MAX 52 /* max subroutine value */
#define DIF_SUBR_JSON 53
#define DIF_SUBR_STRTOLL 54
#define DIF_SUBR_MAX 54 /* max subroutine value */
typedef uint32_t dif_instr_t;

View File

@ -4,6 +4,8 @@ SYSDIR?= ${.CURDIR}/../../..
ARCHDIR= ${MACHINE_CPUARCH}
SUNW= ${.CURDIR}/../../../../cddl/contrib/opensolaris
.PATH: ${SYSDIR}/cddl/contrib/opensolaris/uts/common/dtrace
.PATH: ${SYSDIR}/cddl/compat/opensolaris/kern
.PATH: ${SYSDIR}/cddl/kern
@ -20,7 +22,8 @@ SRCS= dtrace.c \
SRCS+= dis_tables.c \
instr_size.c
CFLAGS+= -I${SYSDIR}/cddl/contrib/opensolaris/uts/intel \
-I${SYSDIR}/cddl/dev/dtrace/x86
-I${SYSDIR}/cddl/dev/dtrace/x86 \
-I${SUNW}/common/util
.endif
SRCS+= bus_if.h device_if.h vnode_if.h