Skip to content

Commit 92f5a47

Browse files
committed
Add optional logging to track down completion issues.
1 parent 20679bc commit 92f5a47

File tree

9 files changed

+170
-16
lines changed

9 files changed

+170
-16
lines changed

TODO.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@ _This todo list describes ChrisAnt996's current intended roadmap for Clink's fut
1313
- ~~Maybe the repro is to queue up typing before the prompt, so that when the prompt shows it starts a coroutine to generate matches (e.g. for suggestions) but then typing and `TAB` is processed while the matches coroutine is already running?~~
1414
- I tried forcing several different race conditions, and none of them could reproduce the issue. It happens only very rarely, so until I can find more detailed context, I can't even tell if it's a recent regression or if it only occurs in a certain configuration. But my guess is it's either a recent regression, or an issue exposed/exacerbated by recent features.
1515
- I just hit it again. I'll have to try to build optional logging to track it down.
16+
- Does not seem to be related to coroutine generators superceding main routine generators (tried injecting os.sleep into coroutine generators to make them take longer, and was unable to repro).
17+
- **What to log:**
18+
- context that triggered running generators
19+
- context that skips generating, and why
20+
- context that cancels generating, and why
21+
- context of timeout of generating (suggestionlist has some kind of timeout IIRC)
22+
- context of reset generate matches
23+
- begin generating
24+
- coroutine or ismain
25+
- volatile
26+
- matches
27+
- generation id
28+
- finish generating
1629
- Some way for `io.popen`, `io.popenyield`, `os.execute`, etc to run without a console window. `clink.execute` exists, but has quirks and doesn't support yielding. This is a problem for any match generators that want to run Powershell, because Powershell insists on changing the window title. Either they have to accept asynchronous window title changes, or they block until the Powershell command finishes. For example, the `pid_complete.lua` module is impacted by this.
1730
- Some way for input hints to show up when the suggestion list is active?
1831
- Make a documentation section that lists all the CLINK environment variables.

clink/lib/include/lib/matches.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ class match_builder
267267

268268
void set_deprecated_mode();
269269
void set_matches_are_files(bool files=true);
270-
void set_input_line(const char* text);
270+
void set_input_line(const char* text, int32 generation_id);
271+
int32 get_generation_id() const;
271272

272273
private:
273274
matches& m_matches;

clink/lib/src/line_editor_impl.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <core/str_iter.h>
3232
#include <core/str_tokeniser.h>
3333
#include <core/settings.h>
34+
#include <core/log.h>
3435
#include <terminal/terminal_in.h>
3536
#include <terminal/terminal_out.h>
3637
#include <terminal/input_idle.h>
@@ -570,13 +571,37 @@ bool line_editor_impl::notify_matches_ready(int32 generation_id, matches* matche
570571
m_matches.transfer(static_cast<matches_impl&>(*matches));
571572
m_matches.done_building();
572573
clear_flag(flag_generate);
574+
if (dbg_get_env_int("CLINK_LOG_GENERATORS"))
575+
{
576+
str<> tmp;
577+
int32 i = 0;
578+
for (matches_iter iter = m_matches.get_iter(); iter.next(); i++)
579+
{
580+
if (i >= 5)
581+
{
582+
tmp.concat(", ...");
583+
break;
584+
}
585+
else if (i > 0)
586+
tmp.concat(", ");
587+
else
588+
tmp.concat(" -> ");
589+
tmp.concat(iter.get_match());
590+
}
591+
assert(generation_id == m_matches.get_generation_id());
592+
LOG("TRANSFERED MATCHES: %u matches, gen %d%s%s", m_matches.get_match_count(), m_matches.get_generation_id(), m_matches.is_volatile() ? ", *VOLATILE*" : "", tmp.c_str());
593+
}
573594
}
574595
else
575596
{
576597
// There's a newer generation id, so force a new suggestion, since the
577598
// newer generation id's autosuggest will have been canceled due to the
578599
// match generator coroutine that was already running, and which has
579600
// just now signaled its completion.
601+
if (dbg_get_env_int("CLINK_LOG_GENERATORS"))
602+
{
603+
LOG("CLEAR MATCHES: matches gen %d vs editor gen %d", generation_id, m_matches_generation_id);
604+
}
580605
clear_suggestion();
581606
}
582607

@@ -1704,6 +1729,11 @@ void line_editor_impl::try_suggest()
17041729
// cannot work with wildcards or substrings.
17051730
rollback<int32> rb_completion_type(rl_completion_type, TAB);
17061731

1732+
if (dbg_get_env_int("CLINK_LOG_GENERATORS"))
1733+
{
1734+
LOG("try_suggest, update_matches");
1735+
}
1736+
17071737
update_matches();
17081738
matches = &m_matches;
17091739
}

clink/lib/src/match_pipeline.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <core/str_compare.h>
1616
#include <core/str_unordered_set.h>
1717
#include <core/settings.h>
18+
#include <core/log.h>
1819
#include <terminal/ecma48_iter.h>
1920

2021
extern "C" {
@@ -334,11 +335,32 @@ void match_pipeline::generate(
334335

335336
m_matches.done_building();
336337

338+
if (dbg_get_env_int("CLINK_LOG_GENERATORS"))
339+
{
340+
str<> tmp;
341+
int32 i = 0;
342+
for (matches_iter iter = m_matches.get_iter(); iter.next(); i++)
343+
{
344+
if (i >= 5)
345+
{
346+
tmp.concat(", ...");
347+
break;
348+
}
349+
else if (i > 0)
350+
tmp.concat(", ");
351+
else
352+
tmp.concat(" -> ");
353+
tmp.concat(iter.get_match());
354+
}
355+
LOG("GENERATED MATCHES: %u matches, gen %d%s%s", m_matches.get_match_count(), m_matches.get_generation_id(), m_matches.is_volatile() ? ", *VOLATILE*" : "", tmp.c_str());
356+
}
357+
337358
#ifdef DEBUG
338359
if (dbg_get_env_int("DEBUG_PIPELINE"))
339360
{
340-
printf("GENERATE, %u matches, word break %u, file_comp %u %s --%s",
361+
printf("GENERATE, %u matches, gen %d, word break %u, file_comp %u %s --%s",
341362
m_matches.get_match_count(),
363+
m_matches.get_generation_id(),
342364
m_matches.get_word_break_position(),
343365
m_matches.is_filename_completion_desired().get(),
344366
m_matches.is_filename_completion_desired().is_explicit() ? "(exp)" : "(imp)",

clink/lib/src/matches_impl.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,15 @@ void match_builder::set_volatile()
334334
}
335335

336336
//------------------------------------------------------------------------------
337-
void match_builder::set_input_line(const char* text)
337+
void match_builder::set_input_line(const char* text, int32 generation_id)
338338
{
339-
return ((matches_impl&)m_matches).set_input_line(text);
339+
return ((matches_impl&)m_matches).set_input_line(text, generation_id);
340+
}
341+
342+
//------------------------------------------------------------------------------
343+
int32 match_builder::get_generation_id() const
344+
{
345+
return ((matches_impl&)m_matches).get_generation_id();
340346
}
341347

342348
//------------------------------------------------------------------------------
@@ -563,8 +569,8 @@ matches_impl::store_impl::store_impl(uint32 size)
563569

564570

565571
//------------------------------------------------------------------------------
566-
matches_impl::matches_impl(uint32 store_size)
567-
: m_store(min(store_size, 0x10000u))
572+
matches_impl::matches_impl()
573+
: m_store(0x10000u)
568574
, m_filename_completion_desired(false)
569575
, m_filename_display_desired(false)
570576
{
@@ -931,6 +937,7 @@ void matches_impl::transfer(matches_impl& from)
931937

932938
m_store = std::move(from.m_store);
933939
m_infos = std::move(from.m_infos);
940+
m_generation_id = from.m_generation_id;
934941
m_count = from.m_count;
935942
m_any_none_type = from.m_any_none_type;
936943
m_deprecated_mode = from.m_deprecated_mode;
@@ -978,6 +985,7 @@ void matches_impl::copy(const matches_impl& from)
978985
m_infos.emplace_back(std::move(add));
979986
}
980987

988+
m_generation_id = from.m_generation_id;
981989
m_count = from.m_count;
982990
m_any_none_type = from.m_any_none_type;
983991
m_deprecated_mode = from.m_deprecated_mode;
@@ -1094,9 +1102,11 @@ void matches_impl::set_volatile()
10941102
}
10951103

10961104
//------------------------------------------------------------------------------
1097-
void matches_impl::set_input_line(const char* text)
1105+
void matches_impl::set_input_line(const char* text, int32 generation_id)
10981106
{
1107+
assert(generation_id > 0);
10991108
m_input_line = text;
1109+
m_generation_id = generation_id;
11001110
}
11011111

11021112
//------------------------------------------------------------------------------

clink/lib/src/matches_impl.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class matches_impl
6262
public:
6363
typedef std::unordered_set<match_lookup, match_lookup_hasher, match_lookup_comparator> match_lookup_unordered_set;
6464

65-
matches_impl(uint32 store_size=0x10000);
65+
matches_impl();
6666
~matches_impl();
6767
matches_iter get_iter() const;
6868
matches_iter get_iter(const char* pattern) const;
@@ -96,6 +96,7 @@ class matches_impl
9696
void set_regen_blocked();
9797
bool is_regen_blocked() const { return m_regen_blocked; }
9898
int32 get_completion_type() const { return m_completion_type; }
99+
int32 get_generation_id() const { return m_generation_id; }
99100

100101
void set_generator(match_generator* generator);
101102
void done_building();
@@ -127,7 +128,7 @@ class matches_impl
127128
void set_no_sort();
128129
void set_has_descriptions();
129130
void set_volatile();
130-
void set_input_line(const char* text);
131+
void set_input_line(const char* text, int32 generation_id);
131132
bool is_from_current_input_line();
132133
bool add_match(const match_desc& desc, bool already_normalised=false);
133134
uint32 get_info_count() const;
@@ -150,6 +151,7 @@ class matches_impl
150151

151152
store_impl m_store;
152153
infos m_infos;
154+
int32 m_generation_id = -1;
153155
unsigned short m_count = 0;
154156
bool m_any_none_type = false;
155157
bool m_deprecated_mode = false;

clink/lua/scripts/generator.lua

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,29 @@ clink.match_display_filter = nil
3333

3434

3535

36+
--------------------------------------------------------------------------------
37+
-- luacheck: globals NOSTK
38+
local nostk_cookie = {}
39+
NOSTK = nostk_cookie
40+
function clink._log_generators(...)
41+
local co, ismain = coroutine.running()
42+
local msg = (ismain and "*" or "")..tostring(co).." ---"
43+
local add_stack = true
44+
for _, e in ipairs({...}) do
45+
if e == nostk_cookie then
46+
add_stack = false
47+
else
48+
msg = msg.." "..tostring(e)
49+
end
50+
end
51+
if add_stack and (tonumber(os.getenv("CLINK_LOG_GENERATORS")) or 0) > 1 then
52+
msg = msg.." --- " .. debug.traceback()
53+
end
54+
log.info(msg)
55+
end
56+
57+
58+
3659
--------------------------------------------------------------------------------
3760
local function advance_ignore_quotes(word, seek)
3861
if seek > 1 then
@@ -122,6 +145,10 @@ local function cancel_match_generate_coroutine()
122145
if ismain and _match_generate_state.coroutine then
123146
-- Make things (e.g. globbers) short circuit to faciliate coroutine
124147
-- completing as quickly as possible.
148+
if os.getenv("CLINK_LOG_GENERATORS") then
149+
clink._log_generators("cancel_match_generate_coroutine", _match_generate_state.coroutine,
150+
"started", _match_generate_state.started and "true" or "false")
151+
end
125152
clink._cancel_coroutine(_match_generate_state.coroutine)
126153
if not _match_generate_state.started then
127154
-- If it never started, remove it from the scheduler.
@@ -138,6 +165,8 @@ function clink._make_match_generate_coroutine(line, lines, matches, builder, gen
138165
return
139166
end
140167

168+
local do_log = os.getenv("CLINK_LOG_GENERATORS")
169+
141170
-- Create coroutine to generate matches. The coroutine is automatically
142171
-- scheduled for resume while waiting for input.
143172
coroutine.override_isgenerator()
@@ -146,6 +175,10 @@ function clink._make_match_generate_coroutine(line, lines, matches, builder, gen
146175
-- started, it can be removed from the scheduler.
147176
_match_generate_state.started = true
148177

178+
if do_log then
179+
clink._log_generators("match_generate_coroutine", "BEGIN", "gen", generation_id)
180+
end
181+
149182
-- Generate matches.
150183
clink._generate(line, lines, builder)
151184

@@ -166,13 +199,27 @@ function clink._make_match_generate_coroutine(line, lines, matches, builder, gen
166199
clink.runonmain(function()
167200
-- PERF: This can potentially take some time, especially in
168201
-- Debug builds.
169-
builder:matches_ready(generation_id)
202+
if do_log then
203+
clink._log_generators("_matches_ready", string.format("(FROM %s)", tostring(c)))
204+
end
205+
builder:_matches_ready(generation_id)
170206
end)
171207
else
172-
builder:clear_toolkit()
208+
if do_log then
209+
clink._log_generators("_clear_toolkit", NOSTK)
210+
end
211+
builder:_clear_toolkit()
212+
end
213+
214+
if do_log then
215+
clink._log_generators("match_generate_coroutine", "END", NOSTK)
173216
end
174217
end)
175218

219+
if do_log then
220+
clink._log_generators("_make_match_generate_coroutine", "->", c, NOSTK)
221+
end
222+
176223
clink.setcoroutinename(c, "generate matches")
177224
_match_generate_state.coroutine = c
178225
_match_generate_state.started = nil
@@ -255,7 +302,14 @@ function clink._generate(line_state, line_states, match_builder, old_filtering)
255302
prepare()
256303
clink.co_state._current_builder = match_builder
257304

305+
local do_log = os.getenv("CLINK_LOG_GENERATORS")
306+
if do_log then
307+
clink._log_generators("clink._generate", "BEGIN", "gen", match_builder:_get_generation_id())
308+
end
258309
local ok, ret = xpcall(impl, _error_handler_ret)
310+
if do_log then
311+
clink._log_generators("clink._generate", "END", NOSTK)
312+
end
259313
if not ok then
260314
print("")
261315
print("match generator failed:")
@@ -268,7 +322,7 @@ function clink._generate(line_state, line_states, match_builder, old_filtering)
268322
end
269323

270324
if not clink._is_coroutine_canceled(coroutine.running()) then
271-
match_builder:set_input_line(line_state:getline())
325+
match_builder:_set_input_line(line_state:getline(), match_builder:_get_generation_id())
272326
end
273327

274328
clink.co_state._current_builder = nil

clink/lua/src/match_builder_lua.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ const match_builder_lua::method match_builder_lua::c_methods[] = {
2626
{ "deprecated_addmatch", &deprecated_add_match },
2727
{ "setmatchesarefiles", &set_matches_are_files },
2828
// UNDOCUMENTED; internal use only.
29-
{ "clear_toolkit", &clear_toolkit },
30-
{ "set_input_line", &set_input_line },
31-
{ "matches_ready", &matches_ready },
29+
{ "_clear_toolkit", &clear_toolkit },
30+
{ "_set_input_line", &set_input_line },
31+
{ "_matches_ready", &matches_ready },
32+
{ "_get_generation_id", &get_generation_id },
3233
{}
3334
};
3435

@@ -351,11 +352,14 @@ int32 match_builder_lua::clear_toolkit(lua_State* state)
351352
// UNDOCUMENTED; internal use only.
352353
int32 match_builder_lua::set_input_line(lua_State* state)
353354
{
355+
if (!m_toolkit)
356+
return 0;
357+
354358
const char* text = checkstring(state, LUA_SELF + 1);
355359
if (!text)
356360
return 0;
357361

358-
m_builder->set_input_line(text);
362+
m_builder->set_input_line(text, m_toolkit->get_generation_id());
359363
return 0;
360364
}
361365

@@ -374,6 +378,23 @@ int32 match_builder_lua::matches_ready(lua_State* state)
374378
return 1;
375379
}
376380

381+
//------------------------------------------------------------------------------
382+
// UNDOCUMENTED; internal use only.
383+
int32 match_builder_lua::get_generation_id(lua_State* state)
384+
{
385+
if (m_toolkit)
386+
{
387+
lua_pushinteger(state, m_toolkit->get_generation_id());
388+
lua_pushliteral(state, "toolkit");
389+
}
390+
else
391+
{
392+
lua_pushinteger(state, m_builder->get_generation_id());
393+
lua_pushliteral(state, "builder");
394+
}
395+
return 2;
396+
}
397+
377398
//------------------------------------------------------------------------------
378399
/// -name: builder:addmatches
379400
/// -ver: 1.0.0

clink/lua/src/match_builder_lua.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class match_builder_lua
4040
int32 clear_toolkit(lua_State* state);
4141
int32 set_input_line(lua_State* state);
4242
int32 matches_ready(lua_State* state);
43+
int32 get_generation_id(lua_State* state);
4344

4445
private:
4546
bool add_match_impl(lua_State* state, int32 stack_index, match_type type);

0 commit comments

Comments
 (0)