Skip to content

Commit 0f490b4

Browse files
author
Guy Bedford
committed
improved debugging
1 parent ffa1179 commit 0f490b4

File tree

9 files changed

+118
-46
lines changed

9 files changed

+118
-46
lines changed

integration-tests/js-compute/fixtures/module-mode/src/http-cache.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,19 @@
22
import { strictEqual, assertRejects } from './assertions.js';
33
import { routes } from './routes.js';
44
import { CacheOverride } from 'fastly:cache-override';
5+
import { Backend } from 'fastly:backend';
56

67
// generate a unique URL everytime so that we never work on a populated cache
78
const getTestUrl = () =>
8-
`https://httpbin.org/anything?${Math.random().toString().slice(2)}`;
9+
`https://httpbin.org/anything/${Math.random().toString().slice(2)}`;
10+
11+
let _httpBinBackend;
12+
const httpBinBackend = () =>
13+
_httpBinBackend ||
14+
(_httpBinBackend = new Backend({
15+
name: `httpbin-${Math.random().toString().slice(2)}`,
16+
target: 'httpbin.org',
17+
}));
918

1019
// Test suite: Error handling
1120
{
@@ -16,6 +25,7 @@ const getTestUrl = () =>
1625
await assertRejects(
1726
() =>
1827
fetch(url, {
28+
backend: httpBinBackend(),
1929
cacheOverride: new CacheOverride({
2030
afterSend(res) {
2131
res.status = 'invalid'; // Should throw type error

integration-tests/js-compute/fixtures/module-mode/src/index.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,14 @@ async function app(event) {
4242
return (res = new Response(
4343
`The routeHandler for ${path} threw a [${error.constructor?.name ?? error.name}] error: ${error.message || error}` +
4444
'\n' +
45-
error.stack,
45+
error.stack +
46+
(fastly.debugMessages
47+
? '\n[DEBUG BUILD MESSAGES]:\n\n - ' +
48+
fastly.debugMessages.join('\n - ')
49+
: ''),
4650
{ status: 500 },
4751
));
4852
} catch (errRes) {
49-
console.error('err2', errRes);
5053
res = errRes;
5154
}
5255
}

integration-tests/js-compute/test.js

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const moduleMode = args.includes('--module-mode');
4040
const aot = args.includes('--aot');
4141
const debugBuild = args.includes('--debug-build');
4242
const filter = args.filter((arg) => !arg.startsWith('--'));
43+
const bail = args.includes('--bail');
4344

4445
async function $(...args) {
4546
return await retry(10, () => zx(...args));
@@ -120,7 +121,7 @@ if (!local) {
120121
if (existsSync(setupPath)) {
121122
core.startGroup('Extra set-up steps for the service');
122123
await zx`node ${setupPath} ${serviceName}`;
123-
await sleep(60);
124+
await sleep(20);
124125
core.endGroup();
125126
}
126127
} else {
@@ -262,31 +263,35 @@ for (const chunk of chunks(Object.entries(tests), 100)) {
262263
}
263264
} else {
264265
if (test.environments.includes('compute')) {
265-
// TODO: this just hides flakes, so we should remove this and fix the flakes.
266-
return retry(10, expBackoff('60s', '10s'), async () => {
267-
let path = test.downstream_request.pathname;
268-
let url = `${domain}${path}`;
269-
try {
270-
const response = await request(url, {
271-
method: test.downstream_request.method || 'GET',
272-
headers: test.downstream_request.headers || undefined,
273-
body: test.downstream_request.body || undefined,
274-
});
275-
const bodyChunks = await getBodyChunks(response);
276-
await compareDownstreamResponse(
277-
test.downstream_response,
278-
response,
279-
bodyChunks,
280-
);
281-
return {
282-
title,
283-
test,
284-
skipped: false,
285-
};
286-
} catch (error) {
287-
throw new Error(`${title} ${error.message}`);
288-
}
289-
});
266+
// TODO: this just hides flakes, so we should remove retry and fix the flakes.
267+
return (bail ? (_, __, fn) => fn() : retry)(
268+
10,
269+
expBackoff('60s', '10s'),
270+
async () => {
271+
let path = test.downstream_request.pathname;
272+
let url = `${domain}${path}`;
273+
try {
274+
const response = await request(url, {
275+
method: test.downstream_request.method || 'GET',
276+
headers: test.downstream_request.headers || undefined,
277+
body: test.downstream_request.body || undefined,
278+
});
279+
const bodyChunks = await getBodyChunks(response);
280+
await compareDownstreamResponse(
281+
test.downstream_response,
282+
response,
283+
bodyChunks,
284+
);
285+
return {
286+
title,
287+
test,
288+
skipped: false,
289+
};
290+
} catch (error) {
291+
throw new Error(`${title} ${error.message}`);
292+
}
293+
},
294+
);
290295
} else {
291296
return {
292297
title,

runtime/fastly/builtins/fastly.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ bool debug_logging_enabled() { return DEBUG_LOGGING_ENABLED; }
3939

4040
namespace fastly::fastly {
4141

42+
JS::PersistentRooted<JSObject *> debugMessages;
4243
JS::PersistentRooted<JSObject *> Fastly::env;
4344
JS::PersistentRooted<JSObject *> Fastly::baseURL;
4445
JS::PersistentRooted<JSString *> Fastly::defaultBackend;
@@ -431,6 +432,19 @@ const JSPropertySpec Fastly::properties[] = {
431432
JS_PSG("sdkVersion", version_get, JSPROP_ENUMERATE),
432433
JS_PS_END};
433434

435+
void push_debug_message(std::string_view msg) {
436+
#ifdef DEBUG
437+
JS::RootedString str(
438+
ENGINE->cx(), JS_NewStringCopyUTF8N(ENGINE->cx(), JS::UTF8Chars(msg.data(), msg.length())));
439+
bool res;
440+
uint32_t len;
441+
res = JS::GetArrayLength(ENGINE->cx(), debugMessages, &len);
442+
MOZ_ASSERT(res);
443+
res = JS_SetElement(ENGINE->cx(), debugMessages, len, str);
444+
MOZ_ASSERT(res);
445+
#endif
446+
}
447+
434448
bool install(api::Engine *engine) {
435449
ENGINE = engine;
436450

@@ -584,6 +598,15 @@ bool install(api::Engine *engine) {
584598
return false;
585599
}
586600

601+
// debugMessages for debug-only builds
602+
#ifdef DEBUG
603+
debugMessages.init(engine->cx(), JS::NewArrayObject(engine->cx(), 0));
604+
RootedValue debug_messages_val(engine->cx(), JS::ObjectValue(*debugMessages));
605+
if (!JS_SetProperty(engine->cx(), fastly, "debugMessages", debug_messages_val)) {
606+
return false;
607+
}
608+
#endif
609+
587610
return true;
588611
}
589612

runtime/fastly/builtins/fastly.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ class Fastly : public builtins::BuiltinNoConstructor<Fastly> {
6161
JS::Result<std::tuple<JS::UniqueChars, size_t>> convertBodyInit(JSContext *cx,
6262
JS::HandleValue bodyInit);
6363

64+
/**
65+
* Debug only logging system, adding messages to `fastly.debugMessages`
66+
*
67+
* This is useful for debugging compute, allowing messages to be inlined into the response in test
68+
* case debugging, where other logging systems may introduce greater latency than this.
69+
*/
70+
void push_debug_message(std::string_view msg);
71+
6472
} // namespace fastly::fastly
6573

6674
#endif

runtime/fastly/builtins/fetch/fetch.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@ bool fetch(JSContext *cx, unsigned argc, Value *vp) {
618618
return false;
619619
}
620620
if (!should_use_guest_caching_out) {
621+
fastly::push_debug_message("Using traditional fetch without cache API");
621622
return fetch_send_body<false>(cx, request, args.rval());
622623
}
623624

@@ -640,6 +641,7 @@ bool fetch(JSContext *cx, unsigned argc, Value *vp) {
640641

641642
// If not cacheable, fallback to non-caching path
642643
if (!is_cacheable) {
644+
fastly::push_debug_message("Request not cacheable, using non-caching fetch");
643645
return fetch_send_body<true>(cx, request, args.rval());
644646
}
645647

@@ -664,6 +666,7 @@ bool fetch(JSContext *cx, unsigned argc, Value *vp) {
664666
auto transaction_res =
665667
host_api::HttpCacheEntry::transaction_lookup(request_handle, override_key_span);
666668
if (auto *err = transaction_res.to_err()) {
669+
fastly::push_debug_message("Transaction lookup error");
667670
if (host_api::error_is_limit_exceeded(*err)) {
668671
JS_ReportErrorASCII(cx, "HTTP caching limit exceeded");
669672
} else {
@@ -680,6 +683,7 @@ bool fetch(JSContext *cx, unsigned argc, Value *vp) {
680683

681684
auto state_res = cache_entry.get_state();
682685
if (auto *err = state_res.to_err()) {
686+
fastly::push_debug_message("Cache state error");
683687
HANDLE_ERROR(cx, *err);
684688
JSObject *promise = PromiseRejectedWithPendingError(cx);
685689
if (!promise) {
@@ -689,10 +693,12 @@ bool fetch(JSContext *cx, unsigned argc, Value *vp) {
689693
return true;
690694
}
691695
auto cache_state = state_res.unwrap();
696+
fastly::push_debug_message(std::to_string(cache_state.state));
692697

693698
// Check for usable cached response
694699
auto found_res = cache_entry.get_found_response(true);
695700
if (auto *err = found_res.to_err()) {
701+
fastly::push_debug_message("Usable cache response error");
696702
HANDLE_ERROR(cx, *err);
697703
JSObject *promise = PromiseRejectedWithPendingError(cx);
698704
if (!promise) {
@@ -704,6 +710,7 @@ bool fetch(JSContext *cx, unsigned argc, Value *vp) {
704710

705711
auto maybe_response = found_res.unwrap();
706712
if (maybe_response.has_value()) {
713+
fastly::push_debug_message("Have usable response");
707714
auto cached_response = maybe_response.value();
708715

709716
if (cache_state.must_insert_or_update()) {
@@ -751,12 +758,15 @@ bool fetch(JSContext *cx, unsigned argc, Value *vp) {
751758

752759
// No valid cached response, need to make backend request
753760
if (!cache_state.must_insert_or_update()) {
761+
fastly::push_debug_message("No usable response, and don't need to insert or update -> pass");
754762
// transaction entry is done
755763
cache_entry.close();
756764
// request collapsing has been disabled: pass the original request to the origin without
757765
// updating the cache and without caching
758766
return fetch_send_body<true>(cx, request, args.rval());
759767
} else {
768+
fastly::push_debug_message(
769+
"No usable response, and must insert or update, running origin fetch hooks");
760770
JS::RootedValue stream_back_promise(cx);
761771
if (!fetch_send_body_with_cache_hooks(cx, request, cache_entry, &stream_back_promise)) {
762772
cache_entry.close();

runtime/fastly/handler.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ void handle_incoming(host_api::Request req) {
4646
}
4747

4848
if (ENGINE->debug_logging_enabled()) {
49-
fastly::fetch_event::dispatch_fetch_event(fetch_event, &total_compute);
49+
fetch_event::dispatch_fetch_event(fetch_event, &total_compute);
5050
} else {
51-
fastly::fetch_event::dispatch_fetch_event(fetch_event);
51+
fetch_event::dispatch_fetch_event(fetch_event);
5252
}
5353

5454
// Loop until no more resolved promises or backend requests are pending.

runtime/fastly/host-api/host_api.cpp

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,26 @@ using fastly::FastlyAPIError;
1717
#ifdef DEBUG
1818

1919
static void log_hostcall(const char *func_name, ...) {
20+
std::stringstream ss;
21+
ss << "HOSTCALL: " << func_name << "(";
22+
2023
va_list args;
2124
va_start(args, func_name);
22-
fprintf(stderr, "HOSTCALL: %s(", func_name);
2325

2426
const auto *arg = va_arg(args, const std::string_view *);
2527
if (arg) {
26-
fprintf(stderr, "\"%.*s\"", (int)arg->size(), arg->data());
28+
ss << std::string_view(arg->data(), arg->size());
2729
while ((arg = va_arg(args, const std::string_view *))) {
28-
fprintf(stderr, ", \"%.*s\"", (int)arg->size(), arg->data());
30+
ss << ", " << std::string_view(arg->data(), arg->size());
2931
}
3032
}
31-
fprintf(stderr, ")\n");
33+
ss << ")";
34+
3235
va_end(args);
33-
fflush(stderr);
36+
37+
// fprintf(stderr, "%s\n", ss.str().c_str());
38+
// Useful for debugging compute to output logs directly to error responses
39+
fastly::fastly::push_debug_message(ss.str());
3440
}
3541

3642
#define TRACE_CALL() log_hostcall(__func__);
@@ -1378,7 +1384,7 @@ Result<HostString> HttpReq::get_method() const {
13781384
}
13791385

13801386
Result<Void> HttpReq::set_uri(std::string_view str) {
1381-
TRACE_CALL()
1387+
TRACE_CALL_ARGS(std::string_view(std::to_string(this->handle)), str)
13821388
Result<Void> res;
13831389

13841390
fastly::fastly_host_error err;
@@ -1930,7 +1936,7 @@ from_fastly_cache_write_options(const fastly::fastly_http_cache_write_options &f
19301936

19311937
// HttpReq cache-related method implementations
19321938
Result<bool> HttpReq::is_cacheable() const {
1933-
TRACE_CALL()
1939+
TRACE_CALL_ARGS(std::string_view(std::to_string(this->handle)))
19341940
uint32_t is_cacheable_out;
19351941
auto res = fastly::http_cache_is_request_cacheable(this->handle, &is_cacheable_out);
19361942
if (res != 0) {
@@ -1985,8 +1991,8 @@ Result<HttpCacheEntry> HttpCacheEntry::lookup(const HttpReq &req, std::span<uint
19851991

19861992
Result<HttpCacheEntry> HttpCacheEntry::transaction_lookup(const HttpReq &req,
19871993
std::span<uint8_t> override_key) {
1988-
TRACE_CALL()
1989-
uint32_t handle_out __attribute__((aligned(4)));
1994+
TRACE_CALL_ARGS(std::string_view(std::to_string(req.handle)))
1995+
alignas(4) uint32_t handle_out;
19901996
fastly::fastly_http_cache_lookup_options opts{};
19911997
uint32_t opts_mask = 0;
19921998

@@ -2207,15 +2213,18 @@ HttpCacheEntry::get_found_response(bool transform_for_client) const {
22072213
}
22082214

22092215
Result<CacheState> HttpCacheEntry::get_state() const {
2210-
TRACE_CALL()
2211-
alignas(4) uint8_t state_out;
2212-
auto res = fastly::http_cache_get_state(this->handle, &state_out);
2216+
TRACE_CALL_ARGS(std::string_view(std::to_string(this->handle)))
2217+
Result<CacheState> res;
22132218

2214-
if (res != 0) {
2215-
return Result<CacheState>::err(host_api::APIError(res));
2219+
fastly::fastly_host_error err;
2220+
alignas(4) uint8_t state;
2221+
if (!convert_result(fastly::http_cache_get_state(this->handle, &state), &err)) {
2222+
res.emplace_err(err);
2223+
} else {
2224+
res.emplace(CacheState{state});
22162225
}
22172226

2218-
return Result<CacheState>::ok(CacheState(state_out));
2227+
return res;
22192228
}
22202229

22212230
Result<uint64_t> HttpCacheEntry::get_length() const {

runtime/fastly/host-api/host_api_fastly.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ struct JSErrorFormatString;
2424

2525
namespace fastly {
2626

27+
namespace fastly {
28+
void push_debug_message(std::string_view msg);
29+
}
30+
2731
const JSErrorFormatString *FastlyGetErrorMessage(void *userRef, unsigned errorNumber);
2832

2933
enum FastlyAPIError {

0 commit comments

Comments
 (0)