Skip to content

Commit f1247af

Browse files
authored
Add USDT for cache directory insertion and deletion (#12688)
1 parent ae00d91 commit f1247af

File tree

4 files changed

+187
-8
lines changed

4 files changed

+187
-8
lines changed

src/iocore/cache/CacheDir.cc

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
#include "tscore/hugepages.h"
3232
#include "tscore/Random.h"
33+
#include "ts/ats_probe.h"
3334

3435
#ifdef LOOP_CHECK_MODE
3536
#define DIR_LOOP_THRESHOLD 1000
@@ -327,6 +328,9 @@ dir_delete_entry(Dir *e, Dir *p, int s, Directory *directory)
327328
} else {
328329
Dir *n = next_dir(e, seg);
329330
if (n) {
331+
// "Shuffle" here means that we're copying the second entry's data to the head entry's location, and removing the second entry
332+
// - because the head entry can't be moved.
333+
ATS_PROBE3(cache_dir_shuffle, s, dir_to_offset(e, seg), dir_to_offset(n, seg));
330334
dir_assign(e, n);
331335
dir_delete_entry(n, e, s, directory);
332336
return e;
@@ -339,7 +343,7 @@ dir_delete_entry(Dir *e, Dir *p, int s, Directory *directory)
339343
}
340344

341345
inline void
342-
dir_clean_bucket(Dir *b, int s, Stripe *stripe)
346+
dir_clean_bucket(Dir *b, int s, StripeSM *stripe)
343347
{
344348
Dir *e = b, *p = nullptr;
345349
Dir *seg = stripe->directory.get_segment(s);
@@ -363,6 +367,8 @@ dir_clean_bucket(Dir *b, int s, Stripe *stripe)
363367
ts::Metrics::Gauge::decrement(cache_rsb.direntries_used);
364368
ts::Metrics::Gauge::decrement(stripe->cache_vol->vol_rsb.direntries_used);
365369
}
370+
// Match cache_dir_remove arguments
371+
ATS_PROBE7(cache_dir_remove_clean_bucket, stripe->fd, s, dir_to_offset(e, seg), dir_offset(e), dir_approx_size(e), 0, 0);
366372
e = dir_delete_entry(e, p, s, &stripe->directory);
367373
continue;
368374
}
@@ -372,7 +378,7 @@ dir_clean_bucket(Dir *b, int s, Stripe *stripe)
372378
}
373379

374380
void
375-
Directory::clean_segment(int s, Stripe *stripe)
381+
Directory::clean_segment(int s, StripeSM *stripe)
376382
{
377383
Dir *seg = this->get_segment(s);
378384
for (int64_t i = 0; i < this->buckets; i++) {
@@ -382,7 +388,7 @@ Directory::clean_segment(int s, Stripe *stripe)
382388
}
383389

384390
void
385-
Directory::cleanup(Stripe *stripe)
391+
Directory::cleanup(StripeSM *stripe)
386392
{
387393
for (int64_t i = 0; i < this->segments; i++) {
388394
this->clean_segment(i, stripe);
@@ -391,7 +397,7 @@ Directory::cleanup(Stripe *stripe)
391397
}
392398

393399
void
394-
Directory::clear_range(off_t start, off_t end, Stripe *stripe)
400+
Directory::clear_range(off_t start, off_t end, StripeSM *stripe)
395401
{
396402
for (off_t i = 0; i < this->entries(); i++) {
397403
Dir *e = dir_index(stripe, i);
@@ -522,6 +528,8 @@ Directory::probe(const CacheKey *key, StripeSM *stripe, Dir *result, Dir **last_
522528
} else { // delete the invalid entry
523529
ts::Metrics::Gauge::decrement(cache_rsb.direntries_used);
524530
ts::Metrics::Gauge::decrement(stripe->cache_vol->vol_rsb.direntries_used);
531+
ATS_PROBE7(cache_dir_remove_invalid, stripe->fd, s, dir_to_offset(e, seg), dir_offset(e), dir_approx_size(e),
532+
key->slice64(0), key->slice64(1));
525533
e = dir_delete_entry(e, p, s, this);
526534
continue;
527535
}
@@ -605,6 +613,8 @@ Directory::insert(const CacheKey *key, StripeSM *stripe, Dir *to_part)
605613
ink_assert(stripe->vol_offset(e) < (stripe->skip + stripe->len));
606614
DDbg(dbg_ctl_dir_insert, "insert %p %X into vol %d bucket %d at %p tag %X %X boffset %" PRId64 "", e, key->slice32(0), stripe->fd,
607615
bi, e, key->slice32(1), dir_tag(e), dir_offset(e));
616+
ATS_PROBE7(cache_dir_insert, stripe->fd, s, dir_to_offset(e, seg), dir_offset(e), dir_approx_size(e), key->slice64(0),
617+
key->slice64(1));
608618
CHECK_DIR(d);
609619
stripe->directory.header->dirty = 1;
610620
ts::Metrics::Gauge::increment(cache_rsb.direntries_used);
@@ -724,9 +734,12 @@ Directory::remove(const CacheKey *key, StripeSM *stripe, Dir *del)
724734
return 0;
725735
}
726736
#endif
727-
if (dir_compare_tag(e, key) && dir_offset(e) == dir_offset(del)) {
737+
int64_t offset = dir_offset(e);
738+
if (dir_compare_tag(e, key) && offset == dir_offset(del)) {
728739
ts::Metrics::Gauge::decrement(cache_rsb.direntries_used);
729740
ts::Metrics::Gauge::decrement(stripe->cache_vol->vol_rsb.direntries_used);
741+
ATS_PROBE7(cache_dir_remove, stripe->fd, s, dir_to_offset(e, seg), offset, dir_approx_size(e), key->slice64(0),
742+
key->slice64(1));
730743
dir_delete_entry(e, p, s, this);
731744
CHECK_DIR(d);
732745
return 1;

src/iocore/cache/P_CacheDir.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,12 +292,12 @@ struct Directory {
292292
int remove(const CacheKey *key, StripeSM *stripe, Dir *del);
293293
void free_entry(Dir *e, int s);
294294
int check();
295-
void cleanup(Stripe *stripe);
296-
void clear_range(off_t start, off_t end, Stripe *stripe);
295+
void cleanup(StripeSM *stripe);
296+
void clear_range(off_t start, off_t end, StripeSM *stripe);
297297
uint64_t entries_used();
298298
int bucket_length(Dir *b, int s);
299299
int freelist_length(int s);
300-
void clean_segment(int s, Stripe *stripe);
300+
void clean_segment(int s, StripeSM *stripe);
301301
};
302302

303303
inline int
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env bpftrace
2+
/** @file
3+
4+
Trace cache directory SystemTap probes.
5+
6+
@section license License
7+
8+
Licensed to the Apache Software Foundation (ASF) under one
9+
or more contributor license agreements. See the NOTICE file
10+
distributed with this work for additional information
11+
regarding copyright ownership. The ASF licenses this file
12+
to you under the Apache License, Version 2.0 (the
13+
"License"); you may not use this file except in compliance
14+
with the License. You may obtain a copy of the License at
15+
16+
http://www.apache.org/licenses/LICENSE-2.0
17+
18+
Unless required by applicable law or agreed to in writing, software
19+
distributed under the License is distributed on an "AS IS" BASIS,
20+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21+
See the License for the specific language governing permissions and
22+
limitations under the License.
23+
*/
24+
25+
BEGIN
26+
{
27+
printf("cache_dir_probe: ready\n");
28+
}
29+
30+
usdt:*:cache_dir_insert
31+
{
32+
if (@inserted == 0) {
33+
printf("cache_dir_insert\n");
34+
}
35+
@inserted = 1;
36+
}
37+
38+
usdt:*:cache_dir_remove
39+
{
40+
if (@removed == 0) {
41+
printf("cache_dir_remove\n");
42+
}
43+
@removed = 1;
44+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
'''Verify cache directory SystemTap probes fire on insert and remove.'''
2+
# Licensed to the Apache Software Foundation (ASF) under one
3+
# or more contributor license agreements. See the NOTICE file
4+
# distributed with this work for additional information
5+
# regarding copyright ownership. The ASF licenses this file
6+
# to you under the Apache License, Version 2.0 (the
7+
# "License"); you may not use this file except in compliance
8+
# with the License. You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
import os
19+
20+
Test.Summary = '''Verify cache directory SystemTap probes fire on cache fill and PURGE.'''
21+
22+
# Skipping this test generally because it requires privilege. Thus most CI systems will skip it.
23+
Test.SkipUnless(
24+
Condition(lambda: os.geteuid() == 0, "Test requires privilege", True),
25+
Condition.HasProgram("bpftrace", "Need bpftrace to verify the probe."))
26+
27+
28+
class CacheDirProbeTest:
29+
'''Verify cache directory SystemTap probes.'''
30+
bt_script: str = 'cache_dir_probe.bt'
31+
_cache_path: str = '/cacheable'
32+
33+
def __init__(self):
34+
tr = Test.AddTestRun('Cache directory probes should trigger on insert and purge.')
35+
self._configure_origin(tr)
36+
self._configure_traffic_server(tr)
37+
self._configure_bpftrace(tr)
38+
self._configure_client(tr)
39+
40+
def _configure_origin(self, tr: 'TestRun') -> 'Process':
41+
'''Configure the origin microserver.'''
42+
origin = Test.MakeOriginServer('origin')
43+
self._origin = origin
44+
45+
cache_request = {
46+
"headers": f"GET {self._cache_path} HTTP/1.1\r\nHost: cache-probe.test\r\n\r\n",
47+
"timestamp": "1469733493.993",
48+
"body": ""
49+
}
50+
cache_response = {
51+
"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\nCache-Control: max-age=120\r\nContent-Length: 5\r\n\r\n",
52+
"timestamp": "1469733493.993",
53+
"body": "hello"
54+
}
55+
origin.addResponse("sessionlog.json", cache_request, cache_response)
56+
origin.addResponse("sessionlog.json", cache_request, cache_response)
57+
58+
purge_request = {
59+
"headers": f"PURGE {self._cache_path} HTTP/1.1\r\nHost: cache-probe.test\r\n\r\n",
60+
"timestamp": "1469733493.993",
61+
"body": ""
62+
}
63+
purge_response = {
64+
"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 0\r\n\r\n",
65+
"timestamp": "1469733493.993",
66+
"body": ""
67+
}
68+
origin.addResponse("sessionlog.json", purge_request, purge_response)
69+
return origin
70+
71+
def _configure_traffic_server(self, tr: 'TestRun') -> 'Process':
72+
'''Configure the Traffic Server process.'''
73+
ts = tr.MakeATSProcess("ts_cache_dir_probe", enable_cache=True)
74+
self._ts = ts
75+
ts.Disk.records_config.update(
76+
{
77+
'proxy.config.diags.debug.enabled': 1,
78+
'proxy.config.diags.debug.tags': 'http|cache',
79+
'proxy.config.http.cache.required_headers': 0,
80+
# Keep ATS running as the invoking user inside sudo (no privilege drop).
81+
'proxy.config.admin.user_id': '#-1',
82+
})
83+
ts.Disk.remap_config.AddLine(f'map / http://127.0.0.1:{self._origin.Variables.Port}')
84+
return ts
85+
86+
def _configure_bpftrace(self, tr: 'TestRun') -> 'Process':
87+
'''Configure the bpftrace process for the cache directory probes.'''
88+
bpftrace = tr.Processes.Process('bpftrace')
89+
self._bpftrace = bpftrace
90+
91+
tr.Setup.Copy(self.bt_script)
92+
tr_script = os.path.join(tr.RunDirectory, self.bt_script)
93+
94+
# fan out output so AuTest stream checks still work
95+
tee_path = os.path.join(tr.RunDirectory, 'bpftrace.out')
96+
bpftrace.Command = f"bpftrace {tr_script}"
97+
bpftrace.ReturnCode = 0
98+
bpftrace.Streams.All += Testers.ContainsExpression('cache_dir_insert', 'cache_dir_insert probe fired.')
99+
bpftrace.Streams.All += Testers.ContainsExpression('cache_dir_remove', 'cache_dir_remove probe fired.')
100+
101+
return bpftrace
102+
103+
def _configure_client(self, tr: 'TestRun') -> 'Process':
104+
'''Configure the client traffic to exercise cache insert and purge.'''
105+
client = tr.Processes.Default
106+
self._client = client
107+
cache_url = f"http://127.0.0.1:{self._ts.Variables.port}{self._cache_path}"
108+
# Ideally we don't need this "sleep 1", but I haven't been able to get it to work with the Ready = When.FileContains(...) approach.
109+
client.Command = (
110+
f"sleep 1 && curl -sSf -o /dev/null -H 'Host: cache-probe.test' {cache_url} && "
111+
f"curl -sSf -o /dev/null -X PURGE -H 'Host: cache-probe.test' {cache_url} && "
112+
f"curl -sSf -o /dev/null -H 'Host: cache-probe.test' {cache_url}")
113+
client.ReturnCode = 0
114+
client.Env = self._ts.Env
115+
116+
self._ts.StartBefore(self._origin) # origin before ts
117+
self._bpftrace.StartBefore(self._ts) # ts before bpftrace
118+
client.StartBefore(self._bpftrace) # bpftrace before client
119+
return client
120+
121+
122+
CacheDirProbeTest()

0 commit comments

Comments
 (0)