@@ -16,6 +16,7 @@ import (
1616
1717 "github.com/digitalocean/go-qemu/qmp/raw"
1818 "github.com/onkernel/hypeman/lib/hypervisor"
19+ "github.com/onkernel/hypeman/lib/logger"
1920 "github.com/onkernel/hypeman/lib/paths"
2021 "gvisor.dev/gvisor/pkg/cleanup"
2122)
@@ -185,6 +186,9 @@ func (s *Starter) StartVM(ctx context.Context, p *paths.Paths, version string, s
185186// RestoreVM starts QEMU and restores VM state from a snapshot.
186187// The VM is in paused state after restore; caller should call Resume() to continue execution.
187188func (s * Starter ) RestoreVM (ctx context.Context , p * paths.Paths , version string , socketPath string , snapshotPath string ) (int , hypervisor.Hypervisor , error ) {
189+ log := logger .FromContext (ctx )
190+ startTime := time .Now ()
191+
188192 // Get binary path
189193 binaryPath , err := s .GetBinaryPath (p , version )
190194 if err != nil {
@@ -201,10 +205,12 @@ func (s *Starter) RestoreVM(ctx context.Context, p *paths.Paths, version string,
201205
202206 // Load saved VM config from snapshot directory
203207 // QEMU requires exact same command-line args as when snapshot was taken
208+ configLoadStart := time .Now ()
204209 config , err := loadVMConfig (snapshotPath )
205210 if err != nil {
206211 return 0 , nil , fmt .Errorf ("load vm config from snapshot: %w" , err )
207212 }
213+ log .DebugContext (ctx , "loaded VM config from snapshot" , "duration_ms" , time .Since (configLoadStart ).Milliseconds ())
208214
209215 instanceDir := filepath .Dir (socketPath )
210216
@@ -249,11 +255,13 @@ func (s *Starter) RestoreVM(ctx context.Context, p *paths.Paths, version string,
249255 cmd .Stdout = vmmLogFile
250256 cmd .Stderr = vmmLogFile
251257
258+ processStartTime := time .Now ()
252259 if err := cmd .Start (); err != nil {
253260 return 0 , nil , fmt .Errorf ("start qemu: %w" , err )
254261 }
255262
256263 pid := cmd .Process .Pid
264+ log .DebugContext (ctx , "QEMU process started" , "pid" , pid , "duration_ms" , time .Since (processStartTime ).Milliseconds ())
257265
258266 // Setup cleanup to kill the process if subsequent steps fail
259267 cu := cleanup .Make (func () {
@@ -262,13 +270,15 @@ func (s *Starter) RestoreVM(ctx context.Context, p *paths.Paths, version string,
262270 defer cu .Clean ()
263271
264272 // Wait for socket to be ready
273+ socketWaitStart := time .Now ()
265274 if err := waitForSocket (socketPath , 10 * time .Second ); err != nil {
266275 vmmLogPath := filepath .Join (logsDir , "vmm.log" )
267276 if logData , readErr := os .ReadFile (vmmLogPath ); readErr == nil && len (logData ) > 0 {
268277 return 0 , nil , fmt .Errorf ("%w; vmm.log: %s" , err , string (logData ))
269278 }
270279 return 0 , nil , err
271280 }
281+ log .DebugContext (ctx , "QMP socket ready" , "duration_ms" , time .Since (socketWaitStart ).Milliseconds ())
272282
273283 // Create QMP client
274284 hv , err := New (socketPath )
@@ -279,12 +289,15 @@ func (s *Starter) RestoreVM(ctx context.Context, p *paths.Paths, version string,
279289 // Wait for incoming migration to complete
280290 // QEMU loads the migration data from the exec subprocess
281291 // After loading, VM is in paused state and ready for 'cont'
292+ migrationWaitStart := time .Now ()
282293 if err := waitForMigrationComplete (hv .client , 30 * time .Second ); err != nil {
283294 return 0 , nil , fmt .Errorf ("wait for migration: %w" , err )
284295 }
296+ log .DebugContext (ctx , "migration complete" , "duration_ms" , time .Since (migrationWaitStart ).Milliseconds ())
285297
286298 // Success - release cleanup to prevent killing the process
287299 cu .Release ()
300+ log .DebugContext (ctx , "QEMU restore complete" , "pid" , pid , "total_duration_ms" , time .Since (startTime ).Milliseconds ())
288301 return pid , hv , nil
289302}
290303
0 commit comments