Skip to content

Commit b3136db

Browse files
bwoebiLeiyks
andauthored
Exclude /vendor/ from code origins (#3399)
* Exclude /vendor/ from code origins Signed-off-by: Bob Weinand <[email protected]> * Preserve line frame numbers Signed-off-by: Bob Weinand <[email protected]> * fix: make vendor case insensitive Signed-off-by: Alexandre Rulleau <[email protected]> --------- Signed-off-by: Bob Weinand <[email protected]> Signed-off-by: Alexandre Rulleau <[email protected]> Co-authored-by: Alexandre Rulleau <[email protected]>
1 parent 939f1ac commit b3136db

File tree

23 files changed

+418
-2711
lines changed

23 files changed

+418
-2711
lines changed

ext/code_origins.c

Lines changed: 65 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,63 +3,86 @@
33
#include "zend_generators.h"
44
#include <Zend/zend_API.h>
55

6+
static inline bool dd_is_dir_sep(const char c) {
7+
#ifdef _WIN32
8+
return c == '/' || c == '\\';
9+
#else
10+
return c == '/';
11+
#endif
12+
}
13+
614
void ddtrace_add_code_origin_information(ddtrace_span_data *span, int skip_frames) {
715
zend_array *meta = ddtrace_property_array(&span->property_meta);
816

9-
zend_execute_data *execute_data = EG(current_execute_data);
1017
zend_long max_frames = get_DD_CODE_ORIGIN_MAX_USER_FRAMES();
11-
int current_frame = 0;
12-
while (execute_data && current_frame < max_frames) {
18+
int current_frame = 0, collected_frames = 0;
19+
for (zend_execute_data *execute_data = EG(current_execute_data); execute_data && collected_frames < max_frames; execute_data = EX(prev_execute_data)) {
1320
if (UNEXPECTED(!EX(func))) {
1421
execute_data = zend_generator_check_placeholder_frame(execute_data);
1522
}
16-
if (EX(func) && ZEND_USER_CODE(EX(func)->type) && EX(func)->op_array.filename) {
17-
if (skip_frames > 0) {
18-
--skip_frames;
19-
} else {
20-
if (current_frame == 0) {
21-
zval type, *kind = zend_hash_str_find_deref(meta, ZEND_STRL("span.kind"));
22-
ZVAL_STRING(&type, (kind && Z_TYPE_P(kind) == IS_STRING ? zend_string_equals_literal(Z_STR_P(kind), "server") || zend_string_equals_literal(Z_STR_P(kind), "producer") : &span->root->span == span) ? "entry" : "exit");
23-
if (!zend_hash_str_add(meta, ZEND_STRL("_dd.code_origin.type"), &type)) {
24-
zend_string_release(Z_STR(type));
25-
return; // skip if already present
26-
}
27-
}
28-
29-
zval zv;
30-
zend_string *key;
23+
if (!EX(func)) {
24+
continue;
25+
}
3126

32-
key = zend_strpprintf(0, "_dd.code_origin.frames.%d.file", current_frame);
33-
ZVAL_STR_COPY(&zv, EX(func)->op_array.filename);
34-
zend_hash_update(meta, key, &zv);
35-
zend_string_release(key);
27+
if (skip_frames > 0) {
28+
--skip_frames;
29+
continue;
30+
}
3631

37-
key = zend_strpprintf(0, "_dd.code_origin.frames.%d.line", current_frame);
38-
ZVAL_LONG(&zv, current_frame == 0 ? EX(func)->op_array.line_start : EX(opline)->lineno);
39-
zend_hash_update(meta, key, &zv);
40-
zend_string_release(key);
32+
if (!ZEND_USER_CODE(EX(func)->type) || !EX(func)->op_array.filename) {
33+
++current_frame;
34+
continue;
35+
}
4136

42-
if (EX(func)->op_array.function_name) {
43-
key = zend_strpprintf(0, "_dd.code_origin.frames.%d.method", current_frame);
44-
ZVAL_STR_COPY(&zv, EX(func)->op_array.function_name);
45-
zend_hash_update(meta, key, &zv);
46-
zend_string_release(key);
47-
}
37+
// Heuristically exclude code outside of the git repository, essentially
38+
const char *vendor = zend_memnistr(ZSTR_VAL(EX(func)->op_array.filename), ZEND_STRL("vendor"), ZSTR_VAL(EX(func)->op_array.filename) + ZSTR_LEN(EX(func)->op_array.filename));
39+
if (vendor && dd_is_dir_sep(vendor[-1]) && dd_is_dir_sep(vendor[6])) {
40+
++current_frame;
41+
continue;
42+
}
4843

49-
if (EX(func)->op_array.scope) {
50-
key = zend_strpprintf(0, "_dd.code_origin.frames.%d.type", current_frame);
51-
ZVAL_STR_COPY(&zv, EX(func)->op_array.scope->name);
52-
zend_hash_update(meta, key, &zv);
53-
zend_string_release(key);
54-
}
55-
++current_frame;
44+
if (collected_frames == 0) {
45+
zval type, *kind = zend_hash_str_find_deref(meta, ZEND_STRL("span.kind"));
46+
ZVAL_STRING(&type, (kind && Z_TYPE_P(kind) == IS_STRING ? zend_string_equals_literal(Z_STR_P(kind), "server") || zend_string_equals_literal(Z_STR_P(kind), "producer") : &span->root->span == span) ? "entry" : "exit");
47+
if (!zend_hash_str_add(meta, ZEND_STRL("_dd.code_origin.type"), &type)) {
48+
zend_string_release(Z_STR(type));
49+
return; // skip if already present
5650
}
5751
}
58-
execute_data = EX(prev_execute_data);
52+
53+
zval zv;
54+
zend_string *key;
55+
56+
key = zend_strpprintf(0, "_dd.code_origin.frames.%d.file", current_frame);
57+
ZVAL_STR_COPY(&zv, EX(func)->op_array.filename);
58+
zend_hash_update(meta, key, &zv);
59+
zend_string_release(key);
60+
61+
key = zend_strpprintf(0, "_dd.code_origin.frames.%d.line", current_frame);
62+
ZVAL_LONG(&zv, current_frame == 0 ? EX(func)->op_array.line_start : EX(opline)->lineno);
63+
zend_hash_update(meta, key, &zv);
64+
zend_string_release(key);
65+
66+
if (EX(func)->op_array.function_name) {
67+
key = zend_strpprintf(0, "_dd.code_origin.frames.%d.method", current_frame);
68+
ZVAL_STR_COPY(&zv, EX(func)->op_array.function_name);
69+
zend_hash_update(meta, key, &zv);
70+
zend_string_release(key);
71+
}
72+
73+
if (EX(func)->op_array.scope) {
74+
key = zend_strpprintf(0, "_dd.code_origin.frames.%d.type", current_frame);
75+
ZVAL_STR_COPY(&zv, EX(func)->op_array.scope->name);
76+
zend_hash_update(meta, key, &zv);
77+
zend_string_release(key);
78+
}
79+
80+
++collected_frames;
81+
++current_frame;
5982
}
6083
}
6184

62-
void ddtrace_maybe_add_code_origin_information(ddtrace_span_data *span) {
85+
void ddtrace_maybe_add_code_origin_information(ddtrace_span_data *span, int skip_frames) {
6386
if (get_DD_CODE_ORIGIN_FOR_SPANS_ENABLED()) {
6487
zval *type = &span->property_type;
6588
ZVAL_DEREF(type);
@@ -80,7 +103,7 @@ void ddtrace_maybe_add_code_origin_information(ddtrace_span_data *span) {
80103
}
81104
}
82105

83-
ddtrace_add_code_origin_information(span, 0);
106+
ddtrace_add_code_origin_information(span, skip_frames);
84107
}
85108
}
86109
}

ext/code_origins.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
#include "span.h"
55

66
void ddtrace_add_code_origin_information(ddtrace_span_data *span, int skip_frames);
7-
void ddtrace_maybe_add_code_origin_information(ddtrace_span_data *span);
7+
void ddtrace_maybe_add_code_origin_information(ddtrace_span_data *span, int skip_frames);
88

99
#endif // DD_CODE_ORIGINS_H

ext/compat_string.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,74 @@ void ddtrace_convert_to_string(zval *dst, zval *src) {
9292
zend_string *str = ddtrace_convert_to_str(src);
9393
ZVAL_STR(dst, str);
9494
}
95+
96+
#if PHP_VERSION_ID < 80200
97+
static zend_always_inline unsigned char dd_tolower_ascii(unsigned char c) {
98+
return (c >= 'A' && c <= 'Z') ? (unsigned char)(c + ('a' - 'A')) : c;
99+
}
100+
101+
static zend_always_inline unsigned char dd_toupper_ascii(unsigned char c) {
102+
return (c >= 'a' && c <= 'z') ? (unsigned char)(c - ('a' - 'A')) : c;
103+
}
104+
105+
static zend_always_inline bool zend_strnieq(const char *ptr1, const char *ptr2, size_t num)
106+
{
107+
const char *end = ptr1 + num;
108+
while (ptr1 < end) {
109+
if (dd_tolower_ascii(*ptr1++) != dd_tolower_ascii(*ptr2++)) {
110+
return 0;
111+
}
112+
}
113+
return 1;
114+
}
115+
116+
const char *zend_memnistr(const char *haystack, const char *needle, size_t needle_len, const char *end)
117+
{
118+
ZEND_ASSERT(end >= haystack);
119+
120+
if (UNEXPECTED(needle_len == 0)) {
121+
return haystack;
122+
}
123+
124+
if (UNEXPECTED(needle_len > (size_t)(end - haystack))) {
125+
return NULL;
126+
}
127+
128+
const char first_lower = dd_tolower_ascii(*needle);
129+
const char first_upper = dd_toupper_ascii(*needle);
130+
const char *p_lower = (const char *)memchr(haystack, first_lower, end - haystack);
131+
const char *p_upper = NULL;
132+
if (first_lower != first_upper) {
133+
// If the needle length is 1 we don't need to look beyond p_lower as it is a guaranteed match
134+
size_t upper_search_length = needle_len == 1 && p_lower != NULL ? p_lower - haystack : end - haystack;
135+
p_upper = (const char *)memchr(haystack, first_upper, upper_search_length);
136+
}
137+
const char *p = !p_upper || (p_lower && p_lower < p_upper) ? p_lower : p_upper;
138+
139+
if (needle_len == 1) {
140+
return p;
141+
}
142+
143+
const char needle_end_lower = dd_tolower_ascii(needle[needle_len - 1]);
144+
const char needle_end_upper = dd_toupper_ascii(needle[needle_len - 1]);
145+
end -= needle_len;
146+
147+
while (p && p <= end) {
148+
if (needle_end_lower == p[needle_len - 1] || needle_end_upper == p[needle_len - 1]) {
149+
if (zend_strnieq(needle + 1, p + 1, needle_len - 2)) {
150+
return p;
151+
}
152+
}
153+
if (p_lower == p) {
154+
p_lower = (const char *)memchr(p_lower + 1, first_lower, end - p_lower);
155+
}
156+
if (p_upper == p) {
157+
p_upper = (const char *)memchr(p_upper + 1, first_upper, end - p_upper);
158+
}
159+
p = !p_upper || (p_lower && p_lower < p_upper) ? p_lower : p_upper;
160+
}
161+
162+
return NULL;
163+
}
164+
165+
#endif

ext/compatibility.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,4 +722,8 @@ extern zend_string *ddtrace_known_strings[ZEND_STR__LAST];
722722

723723
#endif
724724

725+
#if PHP_VERSION_ID < 80200
726+
const char *zend_memnistr(const char *haystack, const char *needle, size_t needle_len, const char *end);
727+
#endif
728+
725729
#endif // DD_COMPATIBILITY_H

ext/ddtrace.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3645,7 +3645,7 @@ PHP_FUNCTION(DDTrace_collect_code_origins) {
36453645
return;
36463646
}
36473647

3648-
ddtrace_add_code_origin_information(span, skip);
3648+
ddtrace_add_code_origin_information(span, skip + 1 /* skip the collect call */);
36493649
}
36503650

36513651
PHP_FUNCTION(DDTrace_startup_logs) {

ext/priority_sampling/priority_sampling.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ static void dd_decide_on_sampling(ddtrace_root_span_data *span) {
278278
}
279279
} else {
280280
LOG(DEBUG, "Evaluated agent sampling rules for root span for trace %s (service: %s, env: %s) and found a sample_rate of %f",
281-
Z_STRVAL(span->property_trace_id), Z_STR_P(service), Z_STR_P(env), zval_get_double(sample_rate_zv));
281+
Z_STRVAL(span->property_trace_id), Z_STRVAL_P(service), Z_STRVAL_P(env), zval_get_double(sample_rate_zv));
282282
}
283283
if (sample_rate_zv) {
284284
sample_rate = zval_get_double(sample_rate_zv);

ext/span.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ void ddtrace_clear_execute_data_span(zend_ulong index, bool keep) {
555555
if (!ddtrace_span_is_dropped(span)) {
556556
if (keep) {
557557
if (&span->root->span != span) {
558-
ddtrace_maybe_add_code_origin_information(span);
558+
ddtrace_maybe_add_code_origin_information(span, 0);
559559
}
560560
ddtrace_close_span(span);
561561
} else {
@@ -859,7 +859,8 @@ void ddtrace_close_span(ddtrace_span_data *span) {
859859
inferred_span->type = DDTRACE_SPAN_CLOSED;
860860
}
861861

862-
ddtrace_maybe_add_code_origin_information(span);
862+
zend_execute_data *execute_data = EG(current_execute_data);
863+
ddtrace_maybe_add_code_origin_information(span, execute_data && EX(func) && !ZEND_USER_CODE(EX(func)->type));
863864
}
864865

865866
if (Z_TYPE(span->property_on_close) != IS_ARRAY || zend_hash_num_elements(Z_ARR(span->property_on_close))) {

loader/tests/functional/test_ddtrace_is_fully_loaded.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@
3636
[type] => system
3737
[meta] => Array
3838
(
39-
[_dd.code_origin.frames.0.file] => %s/fixtures/ddtrace.php
40-
[_dd.code_origin.frames.0.line] => 1
39+
[_dd.code_origin.frames.1.file] => %s/fixtures/ddtrace.php
40+
[_dd.code_origin.frames.1.line] => 13
4141
[_dd.code_origin.type] => exit
4242
[cmd.exit_code] => 0
4343
[cmd.shell] => echo using passthru

tests/Integrations/Guzzle/V6/GuzzleIntegrationTest.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public function providerHttpMethods()
8787

8888
public function testSend()
8989
{
90-
self::putenv('DD_CODE_ORIGIN_MAX_USER_FRAMES=2');
90+
self::putenv('DD_CODE_ORIGIN_MAX_USER_FRAMES=1');
9191
$traces = $this->isolateTracer(function () {
9292
$request = new Request('put', 'http://example.com');
9393
$this->getMockedClient()->send($request);
@@ -102,10 +102,6 @@ public function testSend()
102102
TAG::SPAN_KIND => 'client',
103103
Tag::COMPONENT => 'guzzle',
104104
// Note that the leaf span is guzzle, not the curl inside
105-
'_dd.code_origin.frames.0.file' => '%s/vendor/guzzlehttp/guzzle/src/Client.php',
106-
'_dd.code_origin.frames.0.line' => '%d',
107-
'_dd.code_origin.frames.0.method' => 'send',
108-
'_dd.code_origin.frames.0.type' => 'GuzzleHttp\Client',
109105
'_dd.code_origin.frames.1.file' => '%s/GuzzleIntegrationTest.php',
110106
'_dd.code_origin.frames.1.line' => '%d',
111107
'_dd.code_origin.frames.1.method' => '%s}', // closure format differs on PHP versions

0 commit comments

Comments
 (0)