git.iko.soy/distributed-process
on master help sign in
Do not use (re)register APIs to track trace clients
commitc600749a235018b5a8bf57745d5a57654762aa36 authorTim Watson <tim@rabbitmq.com> dateThu 28 Mar 2013, 17:05 (14 years ago) parentaf229cf
5 files changed+171−137
src/Control/Distributed/Process/Debug.hs+34−34 src/Control/Distributed/Process/Internal/Trace/Primitives.hs+4 src/Control/Distributed/Process/Internal/Trace/Tracer.hs+56−27 src/Control/Distributed/Process/Node.hs+1−2 tests/TestTracing.hs+76−74
src/Control/Distributed/Process/Debug.hs+34−34
@@ -168,6 +168,7 @@ import qualified Control.Distributed.Process.Internal.Trace.Primitives as Tracer
168168 , setTraceFlags
169169 , setTraceFlagsSync
170170 , getTraceFlags
171+ , getCurrentTraceClient
171172 , traceOn
172173 , traceOff
173174 , traceOnly
@@ -195,20 +196,14 @@ import Prelude hiding (catch)
195196-- Only one tracer can be registered at a time, however /this/ function overlays
196197-- the registered tracer with the supplied handler, allowing the user to layer
197198-- multiple tracers on top of one another, with trace events forwarded down
198−-- through all the layers in turn. Once the top layer is stopped, it will
199−-- reregister the original (prior) tracer pid before terminating.
199+-- through all the layers in turn. Once the top layer is stopped, the user
200+-- is responsible for re-registering the original (prior) tracer pid before
201+-- terminating. See 'withTracer' for a mechanism that handles that.
200202startTracer :: (TraceEvent -> Process ()) -> Process ProcessId
201203startTracer handler = do
202204 withRegisteredTracer $ \pid -> do
203205 node <- processNode <$> ask
204− newPid <- liftIO $ forkProcess node $ do
205− -- TODO: we ensure that the original
206− -- tracer is re-registered, but it is
207− -- possible this could lead to races
208− -- if multiple tracers crash in very
209− -- close succession, due to scheduling
210− finally (traceProxy pid handler)
211− (resetTracer pid)
206+ newPid <- liftIO $ forkProcess node $ traceProxy pid handler
212207 enableTrace newPid -- invokes sync + registration
213208 return newPid
214209
@@ -221,20 +216,25 @@ withTracer :: forall a.
221216 -> Process (Either SomeException a)
222217withTracer handler proc = do
223218 say "starting new tracer"
219+ previous <- whereis "tracer"
224220 tracer <- startTracer handler
225− finally (try ((say "evaluating tracer proc...") >> proc))
226− (stopTracing tracer)
221+ finally (try proc)
222+ (stopTracing tracer previous)
227223 where
228− stopTracing :: ProcessId -> Process ()
229− stopTracing tracer = do
230− say "stopping tracer"
231− ref <- monitor tracer
232− stopTracer
233− receiveWait [
234− matchIf (\(ProcessMonitorNotification ref' _ _) -> ref == ref')
235− (\_ -> return ())
236− ]
237− say "stopped..."
224+ stopTracing :: ProcessId -> Maybe ProcessId -> Process ()
225+ stopTracing tracer previousTracer = do
226+ case previousTracer of
227+ Nothing -> return ()
228+ Just p -> do
229+ ref <- monitor tracer
230+ say "resetting to previous tracer" >> enableTrace p
231+ say "stopping tracer"
232+ send tracer TraceEvDisable
233+ say $ "waiting for EXIT from tracer " ++ (show tracer)
234+ receiveWait [
235+ matchIf (\(ProcessMonitorNotification ref' _ _) -> ref == ref')
236+ (\_ -> say "seen EXIT ok")
237+ ]
238238
239239-- | Evaluate @proc@ with the supplied flags enabled. Any previously set
240240-- flags are restored immediately afterwards.
@@ -254,14 +254,9 @@ traceProxy pid act = do
254254 say "tracing has started"
255255 proxy pid $ \(ev :: TraceEvent) ->
256256 case ev of
257− (TraceEvTakeover _) -> die "takeover"
258− TraceEvDisable -> die "disabled"
259− _ -> act ev >> return True
260−
261−resetTracer :: ProcessId -> Process ()
262−resetTracer pid = do
263− say $ "resetting trace to " ++ (show pid)
264− enableTrace pid
257+ (TraceEvTakeover p) -> say ((show p) ++ " has taken over...") >> return False
258+ TraceEvDisable -> say "dying - disabled..." >> die "disabled"
259+ _ -> say ("act on " ++ (show ev)) >> act ev >> return True
265260
266261-- | Stops a user supplied tracer started with 'startTracer'.
267262-- Note that only one tracer process can be active at any given time.
@@ -275,14 +270,17 @@ resetTracer pid = do
275270-- supplied tracers (i.e., processes started via 'startTracer') have exited,
276271-- subsequent calls to this function will have no effect.
277272--
278−-- If tracing is support disabled for the node, this function will also
279−-- have no effect.
273+-- If tracing support is disabled for the node, this function will also
274+-- have no effect. If the last tracer to have been registered (or the process
275+-- registered with the label @"tracer"@ at the time the stop instruction
276+-- is received) was not started with 'startTracer' then the behaviour of
277+-- this function is /undefined/.
280278stopTracer :: Process ()
281279stopTracer =
282280 withRegisteredTracer $ \pid -> do
283281 -- we need to avoid killing the initial (base) tracer, as
284282 -- nothing we rely on having exactly 1 registered tracer
285− -- process at all times!
283+ -- process at all times.
286284 basePid <- whereis "tracer.initial"
287285 case basePid == (Just pid) of
288286 True -> return ()
@@ -290,7 +288,9 @@ stopTracer =
290288
291289withRegisteredTracer :: (ProcessId -> Process a) -> Process a
292290withRegisteredTracer act = do
293− currentTracer <- whereis "tracer"
291+ (sp, rp) <- newChan
292+ withLocalTracer $ \t -> liftIO $ Tracer.getCurrentTraceClient t sp
293+ currentTracer <- receiveChan rp
294294 case currentTracer of
295295 Nothing -> do { (Just p') <- whereis "tracer.initial"; act p' }
296296 (Just p) -> act p
src/Control/Distributed/Process/Internal/Trace/Primitives.hs+4
@@ -18,6 +18,7 @@ module Control.Distributed.Process.Internal.Trace.Primitives
1818 , getTraceFlags
1919 , setTraceFlags
2020 , setTraceFlagsSync
21+ , getCurrentTraceClient
2122 , traceOnly
2223 , traceOn
2324 , traceOff
@@ -98,6 +99,9 @@ setTraceFlagsSync t s f =
9899getTraceFlags :: Tracer -> SendPort TraceFlags -> IO ()
99100getTraceFlags t s = traceIt t (createUnencodedMessage s)
100101
102+getCurrentTraceClient :: Tracer -> SendPort (Maybe ProcessId) -> IO ()
103+getCurrentTraceClient t s = traceIt t (createUnencodedMessage s)
104+
101105-- aux API and utilities
102106
103107class Traceable a where
src/Control/Distributed/Process/Internal/Trace/Tracer.hs+56−27
@@ -27,7 +27,9 @@ import Control.Distributed.Process.Internal.Primitives
2727 , matchAny
2828 , matchAnyIf
2929 , handleMessage
30− , reregister, say
30+ , matchUnknown
31+ , whereis
32+ , reregister, say, unwrapMessage
3133 )
3234import Control.Distributed.Process.Internal.Trace.Types
3335 ( TraceEvent(..)
@@ -87,9 +89,9 @@ import System.Mem.Weak
8789 )
8890
8991data TracerState = TracerST {
90− sendTrace :: !(Message -> Process ())
91− , flags :: !TraceFlags
92− , regNames :: !(Map ProcessId (Set String))
92+ client :: !(Maybe ProcessId)
93+ , flags :: !TraceFlags
94+ , regNames :: !(Map ProcessId (Set String))
9395 }
9496
9597--------------------------------------------------------------------------------
@@ -104,11 +106,25 @@ defaultTracer =
104106defaultTracerAux :: Process ()
105107defaultTracerAux =
106108 catch (checkEnv "DISTRIBUTED_PROCESS_TRACE_CONSOLE" >> systemLoggerTracer)
107− (\(_ :: IOError) -> eventLogTracer)
109+ (\(_ :: IOError) -> defaultEventLogTracer)
110+
111+-- TODO: it would be /nice/ if we had some way of checking the runtime
112+-- options to see if +RTS -v (or similar) has been given...
113+defaultEventLogTracer :: Process ()
114+defaultEventLogTracer =
115+ catch (checkEnv "DISTRIBUTED_PROCESS_TRACE_EVENTLOG" >> eventLogTracer)
116+ (\(_ :: IOError) -> nullTracer)
108117
109118checkEnv :: String -> Process String
110119checkEnv s = liftIO $ getEnv s
111120
121+-- This trace client is (intentionally) useful - it simply provides
122+-- an intial client for the trace controller to talk to, until some
123+-- other (hopefully more useful) client is installed over the top of
124+-- it. This is the default trace client.
125+nullTracer :: Process ()
126+nullTracer = forever' $ receiveWait [ matchUnknown (return ()) ]
127+
112128systemLoggerTracer :: Process ()
113129systemLoggerTracer = do
114130 node <- processNode <$> ask
@@ -171,7 +187,10 @@ traceController mv = do
171187 -- the node's internal control channel if we can possibly help it!
172188 weakQueue <- processWeakQ <$> ask
173189 liftIO $ putMVar mv weakQueue
174− traceLoop initialState
190+ initialTracer <- whereis "tracer.initial"
191+ traceLoop initialState {
192+ client = initialTracer
193+ }
175194 where
176195 traceLoop :: TracerState -> Process ()
177196 traceLoop st = do
@@ -179,22 +198,27 @@ traceController mv = do
179198 -- At some point in the future, we're going to start writing these custom
180199 -- events to the ghc eventlog, at which point this design might change.
181200 st' <- receiveWait [
182− -- we notify the previous tracer process that it has been replaced
183201 match (\(setResp, set :: SetTrace) -> do
184− -- We allow at most one trace target, which is a process id.
202+ -- We allow at most one trace client, which is a process id.
203+ -- Tracking multiple clients represents too high an overhead,
204+ -- so we leave that kind of thing to our consumers to figure
205+ -- figure out.
185206 case set of
186207 (TraceEnable pid) -> do
208+ -- notify the previous tracer it has been replaced
187209 sendTrace st (createUnencodedMessage (TraceEvTakeover pid))
188− reregister "tracer" pid
210+ say $ "registering " ++ (show pid) ++ " as new tracer"
189211 sendOk setResp
190− return st { sendTrace = (mkSender pid) }
212+ return st { client = (Just pid) }
191213 TraceDisable -> do
192214 sendTrace st (createUnencodedMessage TraceEvDisable)
193215 sendOk setResp
194− return st { sendTrace = (\_ -> return ()) })
216+ say "setting sendTrace to noop"
217+ return st { client = Nothing })
195218 , match (\(confResp, flags') ->
196219 sendOk confResp >> applyTraceFlags flags' st)
197220 , match (\chGetFlags -> sendChan chGetFlags (flags st) >> return st)
221+ , match (\chGetCurrent -> sendChan chGetCurrent (client st) >> return st)
198222 -- we dequeue incoming events even if we don't process them
199223 , matchAny (\ev ->
200224 handleMessage ev (handleTrace st ev) >>= return . fromMaybe st)
@@ -205,26 +229,21 @@ traceController mv = do
205229 sendOk Nothing = return ()
206230 sendOk (Just sp) = sendChan sp TraceOk
207231
208− mkSender :: ProcessId -> (Message -> Process ())
209− mkSender pid = \m -> do
210− say ("sending " ++ (show m) ++ " to " ++ (show pid))
211− (flip forward) pid m
212−
213232 initialState :: TracerState
214233 initialState =
215234 TracerST
216− { sendTrace = (\_ -> return ())
217− , flags = defaultTraceFlags
218− , regNames = Map.empty
235+ { client = Nothing
236+ , flags = defaultTraceFlags
237+ , regNames = Map.empty
219238 }
220239
221−-- node [runtime tracer control]
240+-- note [runtime tracer control]
222241--
223242-- The trace mechanism is designed to put as little stress on the system, and
224243-- in particular the node controller, as possible. The LocalNode's @tracer@
225244-- field is therefore immutable, since we don't want to be blocking the node
226245-- controller when writing trace information out. The runtime cost of enabling
227−-- tracing is therefore, the cost of enqueue for all trace-able operations at
246+-- tracing is therefore the cost of enqueue for all trace-able operations at
228247-- all times. If tracing is not explicitly enabled, the trace record stored in
229248-- the local node state is matched and ignored in API calls, reducing the cost
230249-- to a single function call.
@@ -262,13 +281,13 @@ handleTrace st msg ev = do
262281 case ev of
263282 (TraceEvNodeDied _ _) ->
264283 case (traceNodes (flags st)) of
265− True -> (sendTrace st) msg
284+ True -> sendTrace st msg
266285 False -> return ()
267286 (TraceEvUser _) -> do
268− (sendTrace st) msg
287+ sendTrace st msg
269288 _ ->
270289 case (traceConnections (flags st)) of
271− True -> (sendTrace st) msg
290+ True -> sendTrace st msg
272291 False -> return ()
273292 return st
274293
@@ -278,14 +297,14 @@ traceEv :: TraceEvent
278297 -> TracerState
279298 -> Process ()
280299traceEv _ _ Nothing _ = return ()
281−traceEv _ msg (Just TraceAll) st = (sendTrace st) msg
300+traceEv _ msg (Just TraceAll) st = sendTrace st msg
282301traceEv ev msg (Just (TraceProcs pids)) st = do
283302 node <- processNode <$> ask
284303 let p = case resolveToPid ev of
285304 Nothing -> (nullProcessId (localNodeId node))
286305 Just pid -> pid
287306 case (Set.member p pids) of
288− True -> (sendTrace st) msg
307+ True -> sendTrace st msg
289308 False -> return ()
290309traceEv ev msg (Just (TraceNames names)) st = do
291310 -- TODO: if we have recorded regnames for p, then we
@@ -298,5 +317,15 @@ traceEv ev msg (Just (TraceNames names)) st = do
298317 Nothing -> return ()
299318 Just ns -> if (Set.null (Set.intersection ns names))
300319 then return ()
301− else (sendTrace st) msg
320+ else sendTrace st msg
321+
322+sendTrace :: TracerState -> Message -> Process ()
323+sendTrace st msg =
324+ let pid = (client st) in do
325+ (Just ev) <- unwrapMessage msg :: Process (Maybe TraceEvent)
326+ say ("sending " ++ (show ev) ++ " to " ++ (show pid))
327+ case pid of
328+ Just p -> (flip forward) p msg
329+ Nothing -> return ()
330+
302331
src/Control/Distributed/Process/Node.hs+1−2
@@ -272,8 +272,7 @@ startServiceProcesses node = do
272272 mPid <- startDefaultTracer node
273273 case mPid of
274274 Nothing -> return ()
275− Just pid ->
276− runProcess node (register "tracer" pid >> register "tracer.initial" pid)
275+ Just pid -> runProcess node $ register "tracer.initial" pid
277276
278277 logger <- forkProcess node loop
279278 runProcess node $ register "logger" logger
tests/TestTracing.hs+76−74
@@ -5,6 +5,7 @@ module Main where
55import Control.Concurrent.MVar
66 ( MVar
77 , newEmptyMVar
8+ , newMVar
89 , putMVar
910 , takeMVar
1011 )
@@ -17,8 +18,6 @@ import Control.Distributed.Process.Node
1718 , closeLocalNode
1819 , LocalNode)
1920import Control.Exception as Ex (catch)
20−import Data.Binary()
21−import Data.Typeable()
2221import Network.Transport.TCP
2322import Prelude hiding (catch, log)
2423import Test.Framework
@@ -41,12 +40,22 @@ import System.Posix.Env
4140-- | A mutable cell containing a test result.
4241type TestResult a = MVar a
4342
44−delayedAssertion :: (Eq a) => String -> LocalNode -> a ->
45− (TestResult a -> Process ()) -> Assertion
46−delayedAssertion note localNode expected testProc = do
43+delayedAssertion :: (Eq a)
44+ => String
45+ -> LocalNode
46+ -> a
47+ -> (TestResult a -> Process ())
48+ -> MVar ()
49+ -> Assertion
50+delayedAssertion note localNode expected testProc lock = do
4751 result <- newEmptyMVar
48− _ <- forkProcess localNode $ testProc result
52+ _ <- forkProcess localNode $ do
53+ acquire lock
54+ finally (testProc result)
55+ (release lock)
4956 assertComplete note result expected
57+ where acquire lock' = liftIO $ takeMVar lock'
58+ release lock' = liftIO $ putMVar lock' ()
5059
5160assertComplete :: (Eq a) => String -> MVar a -> a -> IO ()
5261assertComplete msg mv a = do
@@ -67,29 +76,36 @@ testSpawnTracing result = do
6776
6877 evSpawned <- liftIO $ newEmptyMVar
6978 evDied <- liftIO $ newEmptyMVar
70− startTracer $ \ev -> do
79+ tracer <- startTracer $ \ev -> do
80+ say "in spawn tracer..."
7181 case ev of
7282 (TraceEvSpawned p) -> liftIO $ putMVar evSpawned p
7383 (TraceEvDied p r) -> liftIO $ putMVar evDied (p, r)
7484 _ -> return ()
7585
7686 (sp, rp) <- newChan
77− pid <- spawnLocal $ do
78− sendChan sp ()
87+ pid <- spawnLocal $ sendChan sp ()
7988 () <- receiveChan rp
8089
8190 tracedAlive <- liftIO $ takeMVar evSpawned
8291 (tracedDead, tracedReason) <- liftIO $ takeMVar evDied
8392
84− stopTracer
93+ mref <- monitor tracer
94+ stopTracer -- this is asynchronous, so we need to wait...
95+ receiveWait [
96+ matchIf (\(ProcessMonitorNotification ref _ _) -> ref == mref)
97+ ((\_ -> return ()))
98+ ]
99+ say "seen the death of tracer"
85100 setTraceFlags defaultTraceFlags
101+ say "finishing setting default flags"
86102 stash result (tracedAlive == pid &&
87103 tracedDead == pid &&
88104 tracedReason == DiedNormal)
89105
90106testTraceRecvExplicitPid :: TestResult Bool -> Process ()
91107testTraceRecvExplicitPid result = do
92− say "testTraceRecvExplicitPid"
108+ res <- liftIO $ newEmptyMVar
93109 pid <- spawnLocal $ do
94110 self <- getSelfPid
95111 expect >>= (flip sendChan) self
@@ -99,19 +115,18 @@ testTraceRecvExplicitPid result = do
99115 withTracer
100116 (\ev ->
101117 case ev of
102− (TraceEvReceived pid' _) -> stash result (pid == pid')
118+ (TraceEvReceived pid' _) -> stash res (pid == pid')
103119 _ -> return ()) $ do
104120 (sp, rp) <- newChan
105121 send pid sp
106122 p <- receiveChan rp
107− res <- liftIO $ takeMVar result
108− stash result (res && (p == pid))
109− say "Explicit done!!!"
123+ res' <- liftIO $ takeMVar res
124+ stash result (res' && (p == pid))
110125 return ()
111126
112127testTraceRecvNamedPid :: TestResult Bool -> Process ()
113128testTraceRecvNamedPid result = do
114− say "testTraceRecvNamedPid"
129+ res <- liftIO $ newEmptyMVar
115130 pid <- spawnLocal $ do
116131 self <- getSelfPid
117132 register "foobar" self
@@ -122,98 +137,85 @@ testTraceRecvNamedPid result = do
122137 withTracer
123138 (\ev ->
124139 case ev of
125− (TraceEvReceived pid' _) -> stash result (pid == pid')
140+ (TraceEvReceived pid' _) -> stash res (pid == pid')
126141 _ -> return ()) $ do
127142 (sp, rp) <- newChan
128143 send pid sp
129144 p <- receiveChan rp
130− res <- liftIO $ takeMVar result
131− stash result (res && (p == pid))
145+ res' <- liftIO $ takeMVar res
146+ stash result (res' && (p == pid))
132147 return ()
133148
134−data TraceWhat = User | Die | Rec
135− deriving (Show)
136−
137149testTraceLayering :: TestResult () -> Process ()
138150testTraceLayering result = do
139151 pid <- spawnLocal $ do
140152 getSelfPid >>= register "foobar"
141− say "registered - waiting for 'hello'"
142− "hello" <- expect
143− say "received 'hello' - waiting for () go"
144− p <- expect
145− say "pid received - sending trace msg"
146− traceMessage ("traceMsg", 123 :: Int)
147− say "sending reply"
148− send p ()
149153 () <- expect
154+ traceMessage ("traceMsg", 123 :: Int)
150155 return ()
151156 withFlags defaultTraceFlags {
152− traceDied = traceOnly ["foobar"]
153− , traceRecv = traceOnly [pid]
157+ traceDied = traceOnly [pid]
158+ , traceRecv = traceOnly ["foobar"]
154159 } $ doTest pid result
155160 return ()
156161 where
157162 doTest :: ProcessId -> MVar () -> Process ()
158163 doTest pid result' = do
159− go Die $ do
160− go Rec $ do
161− say "sending 'hello'"
162− send pid "hello"
163− go User $ do
164− getSelfPid >>= send pid
165− () <- expect
166− say "received reply, returning to rec block"
167− return ()
164+ -- TODO: this is pretty gross, even for a test case
165+ died <- liftIO $ newEmptyMVar
166+ withTracer
167+ (\ev ->
168+ case ev of
169+ TraceEvDied _ _ -> liftIO $ putMVar died ()
170+ _ -> return ())
171+ ( do {
172+ recv <- liftIO $ newEmptyMVar
173+ ; withTracer
174+ (\ev' ->
175+ case ev' of
176+ TraceEvReceived _ _ -> liftIO $ putMVar recv ()
177+ _ -> return ())
178+ ( do {
179+ user <- liftIO $ newEmptyMVar
180+ ; withTracer
181+ (\ev'' ->
182+ case ev'' of
183+ TraceEvUser _ -> liftIO $ putMVar user ()
184+ _ -> return ())
185+ (send pid () >> (liftIO $ takeMVar user))
186+ ; liftIO $ takeMVar recv
187+ })
188+ ; liftIO $ takeMVar died
189+ })
168190 liftIO $ putMVar result' ()
169191
170− go :: TraceWhat -> Process () -> Process ()
171− go what proc = do {
172− say ("tracing " ++ (show what))
173− ; mv <- liftIO $ newEmptyMVar
174− ; r <- (withTracer
175− (\ev ->
176− case (matchTest what ev) of
177− True -> do
178− say ("matched " ++ (show ev))
179− liftIO $ putMVar mv ()
180− False -> say ("ignoring " ++ (show ev))) $ do
181− say ("in block for " ++ (show what))
182− proc)
183− ; case r of
184− Left e -> say "whoops"
185− Right _ -> say "ok!" >> (liftIO $ takeMVar mv)
186− }
187−
188− matchTest :: TraceWhat -> TraceEvent -> Bool
189− matchTest User (TraceEvUser _) = True
190− matchTest Die (TraceEvDied _ _) = True
191− matchTest Rec (TraceEvReceived _ _) = True
192− matchTest _ _ = False
193−
194192tests :: LocalNode -> IO [Test]
195193tests node1 = do
194+ -- if we execute the test cases in parallel, the
195+ -- various tracers will race with one another and
196+ -- we'll get garbage results (or worse, deadlocks)
197+ lock <- liftIO $ newMVar ()
196198 enabled <- checkTraceEnabled
197199 case enabled of
198200 True ->
199201 return [
200− testGroup "Process Info" [
201− testCase "Test Spawn Tracing"
202+ testGroup "Tracing" [
203+ testCase "Spawn Tracing"
202204 (delayedAssertion
203205 "expected dead process-info to be ProcessInfoNone"
204− node1 True testSpawnTracing)
205− , testCase "Test Recv Tracing (Explicit Pid)"
206+ node1 True testSpawnTracing lock)
207+ , testCase "Recv Tracing (Explicit Pid)"
206208 (delayedAssertion
207209 "expected a recv trace for the supplied pid"
208− node1 True testTraceRecvExplicitPid)
209− , testCase "Test Recv Tracing (Named Pid)"
210+ node1 True testTraceRecvExplicitPid lock)
211+ , testCase "Recv Tracing (Named Pid)"
210212 (delayedAssertion
211213 "expected a recv trace for the process registered as 'foobar'"
212− node1 True testTraceRecvNamedPid)
213− , testCase "Test Trace Layering"
214+ node1 True testTraceRecvNamedPid lock)
215+ , testCase "Trace Layering"
214216 (delayedAssertion
215217 "expected blah"
216− node1 () testTraceLayering)
218+ node1 () testTraceLayering lock)
217219 ] ]
218220 False ->
219221 return []