From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id AC71C6EC6F; Fri, 26 Feb 2021 20:16:00 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org AC71C6EC6F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1614359760; bh=fhkIntjYYkoIs99pSyEr/SrfUbbv0hlKcLNKgo9li7k=; h=Date:To:Subject:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=we3kWcxvmS0teh543ZfFyEfAzGaZbdSj8hmoA5CJZ+GTzvWRPM/XJE59191ASwWAO I0hTwthJgHUB0zZo6JOZy+vjdqd+7kPnT5xQL+cqRORBfCYBKJveX8EGouEn5jZNay dlsdnQHFCbu1rPPdbApmVpeN7wUUiY7Y9cXtBbW4= Received: from smtp40.i.mail.ru (smtp40.i.mail.ru [94.100.177.100]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 0A71E6EC6F for ; Fri, 26 Feb 2021 20:15:58 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 0A71E6EC6F Received: by smtp40.i.mail.ru with esmtpa (envelope-from ) id 1lFgic-00012H-3J; Fri, 26 Feb 2021 20:15:58 +0300 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: quoted-printable Mime-Version: 1.0 (Mac OS X Mail 14.0 \(3654.60.0.2.21\)) Message-Id: <1C36EFF6-C8EB-460B-B76F-83C0A3FDFD18@tarantool.org> Date: Fri, 26 Feb 2021 20:15:56 +0300 To: tarantool-patches@dev.tarantool.org X-Mailer: Apple Mail (2.3654.60.0.2.21) X-7564579A: B8F34718100C35BD X-77F55803: 4F1203BC0FB41BD9795828B892398B720FF7727D08DA7A588E6C33AE9CCC9B44182A05F538085040A1702D3606DDE3A83751D51865920B2AE1F6ED11EA6C4358B7531D95CDE8E7A3 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE705C173BDDADFC82AEA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F790063770519A3AF4A224F58638F802B75D45FF914D58D5BE9E6BC131B5C99E7648C95CDDE882590F889B1CF51BC6D9FEFE40B13DF81F53FDBEBF08A471835C12D1D9774AD6D5ED66289B5278DA827A17800CE7A46ADF0C403417779FA2833FD35BB23D2EF20D2F80756B5F868A13BD56FB6657A471835C12D1D977725E5C173C3A84C3E3800B164E348C91117882F4460429728AD0CFFFB425014E868A13BD56FB6657A7F4EDE966BC389F9E8FC8737B5C224960A4683F2394B8D2089D37D7C0E48F6CCF19DD082D7633A0E7DDDDC251EA7DABAAAE862A0553A39223F8577A6DFFEA7CE7876238E5F3EC8143847C11F186F3C5E7DDDDC251EA7DABCC89B49CDF41148FBC2A4A7A8370ED8B3C9F3DD0FB1AF5EB4E70A05D1297E1BBCB5012B2E24CD356 X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975CDDE882590F889B1CF51BC6D9FEFE40B13DF81F53FDBEBF089C2B6934AE262D3EE7EAB7254005DCEDAAE47F5F63E737721E0A4E2319210D9B64D260DF9561598F01A9E91200F654B01098AAFFB0A1231D8E8E86DC7131B365E7726E8460B7C23C X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D343536C6A4332D8B8A650F1FE47210DE79CFC18B8194982F8A2E4D6B344BC3280564AF876239AB06D71D7E09C32AA3244C5C7B02457BF8947737B194F8DCE97630B4DF56057A86259F729B2BEF169E0186 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojbL9S8ysBdXgupTFaWNJDwuRT5AlxgR1M X-Mailru-Sender: 3B9A0136629DC912F4AABCEFC589C81E0D3FD5B043EE97A566363EC90FE8EA2EA0346B1C636DA0E7AD07DD1419AC565FA614486B47F28B67C5E079CCF3B0523AED31B7EB2E253A9E112434F685709FCF0DA7A0AF5A3A8387 X-Mras: Ok Subject: [Tarantool-patches] [PATCH 1/1] rfc: describe an inter-fiber debugger X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Sergey Ostanevich via Tarantool-patches Reply-To: Sergey Ostanevich Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" Subject:=20 An RFC on bringing debugger facility into Tarantool. Part of #5857 --- doc/rfc/inter-fiber-debugger.md | 204 ++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 doc/rfc/inter-fiber-debugger.md diff --git a/doc/rfc/inter-fiber-debugger.md = b/doc/rfc/inter-fiber-debugger.md new file mode 100644 index 000000000..e4b64490c --- /dev/null +++ b/doc/rfc/inter-fiber-debugger.md @@ -0,0 +1,204 @@ +# Inter-fiber Debugger for Tarantool +* **Status**: In progress +* **Start date**: 20-01-2021 +* **Authors**: Sergey Ostanevich @sergos sergos@tarantool.org, + Igor Munkin @imun imun@tarantool.org +* **Discussion**: = https://github.com/tarantool/tarantool/discussions/5857 + +[TOC] + +### Rationale + +To make Tarantool platform developer-friendly we should provide a set = of basic +developer tools. One of such tool is debugger. There are number of = debuggers +available for the Lua environments, although all of them are missing = the +critical feature needed for the Tarantool platform: they should not = cause a +full-stop of the debugged program during the debug session. + +In this RFC I propose to overcome the problem with a solution that will = stop +only the fiber to be debugged. It will allow developers to debug their +application, while Tarantool can keep processing requests, perform = replication +and so on. + +### Approach + +To do not reinvent the debugger techniques we may borrow the already = existent +Lua debugger, put the rules about fiber use, data manipulation tweaks = and so +on. + +Every fiber can be considered as a 'debuggee' or a regular fiber, = switching +from one state to the other. To control the status we can either patch = fiber +machinery - which seems excessive as fibers can serve pure C tasks - or = tweak +the breakpoint hook to employ the fiber yield. The fiber will appear in = a state +it waits for commands from the debugger and set the LuaJIT machinery = hooks to +be prepared for the next fiber to be scheduled. + +### Debug techniques + +Regular debuggers provide interruption for all threads at once hence = they don't +distinguish breakpoints appearance across the threads - they just stop +execution. For our case we have to introduce some specifics so that = debugger +will align with the fiber nature of the server behavior. Let's consider = some +techniques we can propose to the user. + +#### 1) Break first fiber met + +User puts a breakpoint that triggers once, stopping the first fiber the = break +happens in. After breakpoint is met the fiber reports its status to the +debugger server, put itself in a wait state, clears the breakpoint and = yields. +As soon as server issue a command, the debuggee will reset the = breakpoint, +handle the command and proceed with execution or yield again. + +#### 2) Regular breakpoint + +This mode will start the same way as previous mode, but keep the = breakpoint +before yield, so that the breakpoint still can trigger in another = fiber. As the +server may deliver huge number of fibers during its performance, we = have to set +up a user-configurable limit for the number of debuggee fibers can be = set at +once. As soon as limit is reached the debuggee fiber starts behave = exactly as +in previous mode, clearing the breakpoint before the yield from the = debuggee. + +#### 3) Run a function under debug session + +This is the most straightforward way to debug a function: perform a = call +through the debug interface. A new fiber will be created and break will = appear +at the function entrance. The limit of debuggee fibers should be = increased and +the fiber will behave similar to the modes above. + +#### 4) Attach debugger to a fiber by ID + +Every fiber has its numerical ID, so debugger can provide interface to = start +debugging for a particular fiber. The fiber will be put in a wait state = as soon +as it start execution after the debugger is attached. + +### Basic mechanism + +The Tarantool side of the debugger will consist of a dedicated fiber = named +DebugSRV that will handle requests from the developer and make = bookkeeping of +debuggee fibers and their breakpoints and a Lua function DebugHook set = as a +hook in Lua debug [https://www.lua.org/pil/23.html] library. Users = should not +use this hook for the period of debugging to avoid interference. The = external +interface can be organized over arbitrary protocol, be it a socket = connection, +console or even IPROTO (using IPROTO_CALL). + +Debuggee fiber will be controlled by a debug hook function named = DebugHook. It +is responsibility of the DebugHook to set the debuggee fiber status, = check the +breakpoints appearance, its condition including the ignore count and = update +hit_count. As soon as breakpoint is met, the DebugHook has to put its = state to +pending and wait for command from the DebugSRV. + +Communication between DebugSRV and the debuggee fiber can be done via +fiber.channel mechanism. It will simplify the wait-for semantics. + +#### Data structure + +Every debuggee fiber is present in the corresponding table in the = DebugSRV +fiber. The table has the following format: + +``` +debuggees =3D { + max_debuggee =3D number, + preserved_hook =3D { + [1] =3D function, + [2] =3D type, + [3] =3D number + } + fibers =3D { + [] =3D { + state =3D ['pending'|'operation'], + current_breakpoint =3D , + channel =3D fiber.channel, + breakpoints =3D { + [] =3D { + type =3D ['l'|'c'|'r'|'i'], + value =3D [number|string] + condition =3D function, + hit_count =3D number, + ignore_count =3D number + } + } + } + } + global_breakpoints =3D { + [] =3D { + type =3D ['l'|'c'|'r'|'i'], + value =3D [number|string] + condition =3D function, + hit_count =3D number, + ignore_count =3D number + } +} +``` +As DebugSRV receives commands it updates the structure of the debuggees = and +forces the fiber wakeup to reset its hook state. The state of the = debuggee is +one of the following: + +- 'operation': the fiber is already in the debuggees list, but it = issued yield + without any breakpoint met +- 'pending': DebugHook waits for a new command from the channel in the + debuggees.fibers of its own ID + + +#### DebugHook behavior + +For the techniques 3) and 4) fiber appears in the list of = debuggees.fibers +first, with its status set as 'operation' with a list of breakpoints = set. + +For the techniques 1) and 2) there is a list of global_breakpoints that = should +be checked by every fiber. + +In case a fiber receives control from the debug machinery it should = check if it +is present in ```debuggees.fibers[ID]```. If it is - it should check if = its +current position meets any breakpoint from the +```debuggees.fibers[ID].breakpoints``` or = ```debuggees.global_breakponts```. If +breakpoint is met, the fiber sets its state into 'pending' and waits = for a +command from the ```debuggees.fibers[ID].channel```. + +In case a fiber is not present in the ```debuggees.fibers[ID]``` it = should +check that the number of fibers entries in the debuggees structure is = less than +max_debuggee. In such a case it checks if it met any of the +```global_breakpoint``` it and put itself into the fibers list, = updating the +array size [https://www.lua.org/pil/19.1.html]. Also it should open a = channel +to the DebugSVR and put itself into the 'pending' state. + +#### DebugSRV behavior + +DebugSRV handles the input from the user and supports the following = list of +commands (as mentioned, it can be used from any interface, so commands = are +function calls for general case): + +- ```break_info([fiber ID])``` - list all breakpoints with counts and + conditions, limits output for the fiber with ID +- ```break_cond(, )``` - set a condition for = the + breakpoint, condition should be Lua code evaluating into a boolean = value +- ```break_ignore(, )``` - ignore the number of + breakpoint executions +- ```break_delete()``` - removes a breakpoint +- ```step()``` - continue execution, stepping into the call +- ```step_over()``` - continue execution until the next = source line, + skip calls +- ```step_out()``` - continue execution until return from the = current + function + +The functions above are common for many debuggers, just some tweaks to = adopt +fibers. Functions below are more specific, so let's get into some = details: + +- ```set_max_debuggee(number)``` - set the number of fibers can be = debugged + simultaneously. It modifies the ```debuggees.max_debuggee``` so that = new fibers + will respect the amount of debuggees. For example, if at some point = of + debugging there were 5 debuggee fibers user can set this value to 3 - = it will + not cause any problem, just a new fiber will not become a debuggee if = it meet + some global breakpoint. +- ```debug_eval(, )``` - allows to evaluate the code in = the + context of the debuggee fiber if it is in 'pending' mode. User can = issue a + ```debug_eval(113, function() return fiber.id() end)``` to receive = 113 as a + result +- ```break(, [fiber ID])``` - add a new = breakpoint in + the fiber's breakpoint list on in the global list if no fiber ID = provided +- ```debug_start()``` - starts debug session: creates debuggees = structure, + preserve current debug hook in ```debuggees.preserved_hook``` and = sets + DebugHook as the current hook +- ```debug_stop()``` - quits debug session: resets the debug hook, = clears + debuggees structure + + -- 2.24.3 (Apple Git-128)