feat(tvix/eval): give generators human-readable names
This adds static strings to generator frames that describe the generator in a human-readable fashion, which are then logged in observers. This makes runtime traces very precise, explaining exactly what is being requested from where. Change-Id: I695659a6bd0b7b0bdee75bc8049651f62b150e0c Reviewed-on: https://cl.tvl.fyi/c/depot/+/8206 Tested-by: BuildkiteCI Reviewed-by: raitobezarius <tvl@lahfa.xyz>
This commit is contained in:
		
							parent
							
								
									dfd0066de5
								
							
						
					
					
						commit
						1e37f8b52e
					
				
					 5 changed files with 84 additions and 55 deletions
				
			
		|  | @ -217,8 +217,9 @@ pub fn pin_generator( | |||
| impl<'o> VM<'o> { | ||||
|     /// Helper function to re-enqueue the current generator while it
 | ||||
|     /// is awaiting a value.
 | ||||
|     fn reenqueue_generator(&mut self, span: LightSpan, generator: Generator) { | ||||
|     fn reenqueue_generator(&mut self, name: &'static str, span: LightSpan, generator: Generator) { | ||||
|         self.frames.push(Frame::Generator { | ||||
|             name, | ||||
|             generator, | ||||
|             span, | ||||
|             state: GeneratorState::AwaitingValue, | ||||
|  | @ -226,12 +227,13 @@ impl<'o> VM<'o> { | |||
|     } | ||||
| 
 | ||||
|     /// Helper function to enqueue a new generator.
 | ||||
|     pub(super) fn enqueue_generator<F, G>(&mut self, span: LightSpan, gen: G) | ||||
|     pub(super) fn enqueue_generator<F, G>(&mut self, name: &'static str, span: LightSpan, gen: G) | ||||
|     where | ||||
|         F: Future<Output = Result<Value, ErrorKind>> + 'static, | ||||
|         G: FnOnce(GenCo) -> F, | ||||
|     { | ||||
|         self.frames.push(Frame::Generator { | ||||
|             name, | ||||
|             span, | ||||
|             state: GeneratorState::Running, | ||||
|             generator: Gen::new(|co| pin_generator(gen(co))), | ||||
|  | @ -245,6 +247,7 @@ impl<'o> VM<'o> { | |||
|     /// or was suspended (false).
 | ||||
|     pub(crate) fn run_generator( | ||||
|         &mut self, | ||||
|         name: &'static str, | ||||
|         span: LightSpan, | ||||
|         frame_id: usize, | ||||
|         state: GeneratorState, | ||||
|  | @ -266,7 +269,7 @@ impl<'o> VM<'o> { | |||
|                 // If the generator yields, it contains an instruction
 | ||||
|                 // for what the VM should do.
 | ||||
|                 genawaiter::GeneratorState::Yielded(request) => { | ||||
|                     self.observer.observe_generator_request(&request); | ||||
|                     self.observer.observe_generator_request(name, &request); | ||||
| 
 | ||||
|                     match request { | ||||
|                         GeneratorRequest::StackPush(value) => { | ||||
|  | @ -282,15 +285,17 @@ impl<'o> VM<'o> { | |||
|                         // this function prepares the frame stack and yields
 | ||||
|                         // back to the outer VM loop.
 | ||||
|                         GeneratorRequest::ForceValue(value) => { | ||||
|                             self.reenqueue_generator(span.clone(), generator); | ||||
|                             self.enqueue_generator(span, |co| value.force(co)); | ||||
|                             self.reenqueue_generator(name, span.clone(), generator); | ||||
|                             self.enqueue_generator("force", span, |co| value.force(co)); | ||||
|                             return Ok(false); | ||||
|                         } | ||||
| 
 | ||||
|                         // Generator has requested a deep-force.
 | ||||
|                         GeneratorRequest::DeepForceValue(value, thunk_set) => { | ||||
|                             self.reenqueue_generator(span.clone(), generator); | ||||
|                             self.enqueue_generator(span, |co| value.deep_force(co, thunk_set)); | ||||
|                             self.reenqueue_generator(name, span.clone(), generator); | ||||
|                             self.enqueue_generator("deep_force", span, |co| { | ||||
|                                 value.deep_force(co, thunk_set) | ||||
|                             }); | ||||
|                             return Ok(false); | ||||
|                         } | ||||
| 
 | ||||
|  | @ -298,10 +303,10 @@ impl<'o> VM<'o> { | |||
|                         // Logic is similar to `ForceValue`, except with the
 | ||||
|                         // value being taken from that stack.
 | ||||
|                         GeneratorRequest::WithValue(idx) => { | ||||
|                             self.reenqueue_generator(span.clone(), generator); | ||||
|                             self.reenqueue_generator(name, span.clone(), generator); | ||||
| 
 | ||||
|                             let value = self.stack[self.with_stack[idx]].clone(); | ||||
|                             self.enqueue_generator(span, |co| value.force(co)); | ||||
|                             self.enqueue_generator("force", span, |co| value.force(co)); | ||||
| 
 | ||||
|                             return Ok(false); | ||||
|                         } | ||||
|  | @ -310,34 +315,36 @@ impl<'o> VM<'o> { | |||
|                         // with-stack. Logic is same as above, except for the
 | ||||
|                         // value being from that stack.
 | ||||
|                         GeneratorRequest::CapturedWithValue(idx) => { | ||||
|                             self.reenqueue_generator(span.clone(), generator); | ||||
|                             self.reenqueue_generator(name, span.clone(), generator); | ||||
| 
 | ||||
|                             let call_frame = self.last_call_frame() | ||||
|                                 .expect("Tvix bug: generator requested captured with-value, but there is no call frame"); | ||||
| 
 | ||||
|                             let value = call_frame.upvalues.with_stack().unwrap()[idx].clone(); | ||||
|                             self.enqueue_generator(span, |co| value.force(co)); | ||||
|                             self.enqueue_generator("force", span, |co| value.force(co)); | ||||
| 
 | ||||
|                             return Ok(false); | ||||
|                         } | ||||
| 
 | ||||
|                         GeneratorRequest::NixEquality(values, ptr_eq) => { | ||||
|                             let values = *values; | ||||
|                             self.reenqueue_generator(span.clone(), generator); | ||||
|                             self.enqueue_generator(span, |co| { | ||||
|                             self.reenqueue_generator(name, span.clone(), generator); | ||||
|                             self.enqueue_generator("nix_eq", span, |co| { | ||||
|                                 values.0.nix_eq(values.1, co, ptr_eq) | ||||
|                             }); | ||||
|                             return Ok(false); | ||||
|                         } | ||||
| 
 | ||||
|                         GeneratorRequest::StringCoerce(val, kind) => { | ||||
|                             self.reenqueue_generator(span.clone(), generator); | ||||
|                             self.enqueue_generator(span, |co| val.coerce_to_string(co, kind)); | ||||
|                             self.reenqueue_generator(name, span.clone(), generator); | ||||
|                             self.enqueue_generator("coerce_to_string", span, |co| { | ||||
|                                 val.coerce_to_string(co, kind) | ||||
|                             }); | ||||
|                             return Ok(false); | ||||
|                         } | ||||
| 
 | ||||
|                         GeneratorRequest::Call(callable) => { | ||||
|                             self.reenqueue_generator(span.clone(), generator); | ||||
|                             self.reenqueue_generator(name, span.clone(), generator); | ||||
|                             self.tail_call_value(span, None, callable)?; | ||||
|                             return Ok(false); | ||||
|                         } | ||||
|  | @ -347,7 +354,7 @@ impl<'o> VM<'o> { | |||
|                             upvalues, | ||||
|                             light_span, | ||||
|                         } => { | ||||
|                             self.reenqueue_generator(span, generator); | ||||
|                             self.reenqueue_generator(name, span, generator); | ||||
| 
 | ||||
|                             self.frames.push(Frame::CallFrame { | ||||
|                                 span: light_span, | ||||
|  | @ -423,14 +430,14 @@ impl<'o> VM<'o> { | |||
| 
 | ||||
|                         GeneratorRequest::TryForce(value) => { | ||||
|                             self.try_eval_frames.push(frame_id); | ||||
|                             self.reenqueue_generator(span.clone(), generator); | ||||
|                             self.reenqueue_generator(name, span.clone(), generator); | ||||
| 
 | ||||
|                             debug_assert!( | ||||
|                                 self.frames.len() == frame_id + 1, | ||||
|                                 "generator should be reenqueued with the same frame ID" | ||||
|                             ); | ||||
| 
 | ||||
|                             self.enqueue_generator(span, |co| value.force(co)); | ||||
|                             self.enqueue_generator("force", span, |co| value.force(co)); | ||||
|                             return Ok(false); | ||||
|                         } | ||||
|                     } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue