...

Source file src/internal/trace/oldtrace.go

Documentation: internal/trace

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // This file implements conversion from old (Go 1.11–Go 1.21) traces to the Go
     6  // 1.22 format.
     7  //
     8  // Most events have direct equivalents in 1.22, at worst requiring arguments to
     9  // be reordered. Some events, such as GoWaiting need to look ahead for follow-up
    10  // events to determine the correct translation. GoSyscall, which is an
    11  // instantaneous event, gets turned into a 1 ns long pair of
    12  // GoSyscallStart+GoSyscallEnd, unless we observe a GoSysBlock, in which case we
    13  // emit a GoSyscallStart+GoSyscallEndBlocked pair with the correct duration
    14  // (i.e. starting at the original GoSyscall).
    15  //
    16  // The resulting trace treats the old trace as a single, large generation,
    17  // sharing a single evTable for all events.
    18  //
    19  // We use a new (compared to what was used for 'go tool trace' in earlier
    20  // versions of Go) parser for old traces that is optimized for speed, low memory
    21  // usage, and minimal GC pressure. It allocates events in batches so that even
    22  // though we have to load the entire trace into memory, the conversion process
    23  // shouldn't result in a doubling of memory usage, even if all converted events
    24  // are kept alive, as we free batches once we're done with them.
    25  //
    26  // The conversion process is lossless.
    27  
    28  package trace
    29  
    30  import (
    31  	"errors"
    32  	"fmt"
    33  	"internal/trace/event"
    34  	"internal/trace/event/go122"
    35  	"internal/trace/internal/oldtrace"
    36  	"io"
    37  )
    38  
    39  type oldTraceConverter struct {
    40  	trace          oldtrace.Trace
    41  	evt            *evTable
    42  	preInit        bool
    43  	createdPreInit map[GoID]struct{}
    44  	events         oldtrace.Events
    45  	extra          []Event
    46  	extraArr       [3]Event
    47  	tasks          map[TaskID]taskState
    48  	seenProcs      map[ProcID]struct{}
    49  	lastTs         Time
    50  	procMs         map[ProcID]ThreadID
    51  	lastStwReason  uint64
    52  
    53  	inlineToStringID  []uint64
    54  	builtinToStringID []uint64
    55  }
    56  
    57  const (
    58  	// Block reasons
    59  	sForever = iota
    60  	sPreempted
    61  	sGosched
    62  	sSleep
    63  	sChanSend
    64  	sChanRecv
    65  	sNetwork
    66  	sSync
    67  	sSyncCond
    68  	sSelect
    69  	sEmpty
    70  	sMarkAssistWait
    71  
    72  	// STW kinds
    73  	sSTWUnknown
    74  	sSTWGCMarkTermination
    75  	sSTWGCSweepTermination
    76  	sSTWWriteHeapDump
    77  	sSTWGoroutineProfile
    78  	sSTWGoroutineProfileCleanup
    79  	sSTWAllGoroutinesStackTrace
    80  	sSTWReadMemStats
    81  	sSTWAllThreadsSyscall
    82  	sSTWGOMAXPROCS
    83  	sSTWStartTrace
    84  	sSTWStopTrace
    85  	sSTWCountPagesInUse
    86  	sSTWReadMetricsSlow
    87  	sSTWReadMemStatsSlow
    88  	sSTWPageCachePagesLeaked
    89  	sSTWResetDebugLog
    90  
    91  	sLast
    92  )
    93  
    94  func (it *oldTraceConverter) init(pr oldtrace.Trace) error {
    95  	it.trace = pr
    96  	it.preInit = true
    97  	it.createdPreInit = make(map[GoID]struct{})
    98  	it.evt = &evTable{pcs: make(map[uint64]frame)}
    99  	it.events = pr.Events
   100  	it.extra = it.extraArr[:0]
   101  	it.tasks = make(map[TaskID]taskState)
   102  	it.seenProcs = make(map[ProcID]struct{})
   103  	it.procMs = make(map[ProcID]ThreadID)
   104  	it.lastTs = -1
   105  
   106  	evt := it.evt
   107  
   108  	// Convert from oldtracer's Strings map to our dataTable.
   109  	var max uint64
   110  	for id, s := range pr.Strings {
   111  		evt.strings.insert(stringID(id), s)
   112  		if id > max {
   113  			max = id
   114  		}
   115  	}
   116  	pr.Strings = nil
   117  
   118  	// Add all strings used for UserLog. In the old trace format, these were
   119  	// stored inline and didn't have IDs. We generate IDs for them.
   120  	if max+uint64(len(pr.InlineStrings)) < max {
   121  		return errors.New("trace contains too many strings")
   122  	}
   123  	var addErr error
   124  	add := func(id stringID, s string) {
   125  		if err := evt.strings.insert(id, s); err != nil && addErr == nil {
   126  			addErr = err
   127  		}
   128  	}
   129  	for id, s := range pr.InlineStrings {
   130  		nid := max + 1 + uint64(id)
   131  		it.inlineToStringID = append(it.inlineToStringID, nid)
   132  		add(stringID(nid), s)
   133  	}
   134  	max += uint64(len(pr.InlineStrings))
   135  	pr.InlineStrings = nil
   136  
   137  	// Add strings that the converter emits explicitly.
   138  	if max+uint64(sLast) < max {
   139  		return errors.New("trace contains too many strings")
   140  	}
   141  	it.builtinToStringID = make([]uint64, sLast)
   142  	addBuiltin := func(c int, s string) {
   143  		nid := max + 1 + uint64(c)
   144  		it.builtinToStringID[c] = nid
   145  		add(stringID(nid), s)
   146  	}
   147  	addBuiltin(sForever, "forever")
   148  	addBuiltin(sPreempted, "preempted")
   149  	addBuiltin(sGosched, "runtime.Gosched")
   150  	addBuiltin(sSleep, "sleep")
   151  	addBuiltin(sChanSend, "chan send")
   152  	addBuiltin(sChanRecv, "chan receive")
   153  	addBuiltin(sNetwork, "network")
   154  	addBuiltin(sSync, "sync")
   155  	addBuiltin(sSyncCond, "sync.(*Cond).Wait")
   156  	addBuiltin(sSelect, "select")
   157  	addBuiltin(sEmpty, "")
   158  	addBuiltin(sMarkAssistWait, "GC mark assist wait for work")
   159  	addBuiltin(sSTWUnknown, "")
   160  	addBuiltin(sSTWGCMarkTermination, "GC mark termination")
   161  	addBuiltin(sSTWGCSweepTermination, "GC sweep termination")
   162  	addBuiltin(sSTWWriteHeapDump, "write heap dump")
   163  	addBuiltin(sSTWGoroutineProfile, "goroutine profile")
   164  	addBuiltin(sSTWGoroutineProfileCleanup, "goroutine profile cleanup")
   165  	addBuiltin(sSTWAllGoroutinesStackTrace, "all goroutine stack trace")
   166  	addBuiltin(sSTWReadMemStats, "read mem stats")
   167  	addBuiltin(sSTWAllThreadsSyscall, "AllThreadsSyscall")
   168  	addBuiltin(sSTWGOMAXPROCS, "GOMAXPROCS")
   169  	addBuiltin(sSTWStartTrace, "start trace")
   170  	addBuiltin(sSTWStopTrace, "stop trace")
   171  	addBuiltin(sSTWCountPagesInUse, "CountPagesInUse (test)")
   172  	addBuiltin(sSTWReadMetricsSlow, "ReadMetricsSlow (test)")
   173  	addBuiltin(sSTWReadMemStatsSlow, "ReadMemStatsSlow (test)")
   174  	addBuiltin(sSTWPageCachePagesLeaked, "PageCachePagesLeaked (test)")
   175  	addBuiltin(sSTWResetDebugLog, "ResetDebugLog (test)")
   176  
   177  	if addErr != nil {
   178  		// This should be impossible but let's be safe.
   179  		return fmt.Errorf("couldn't add strings: %w", addErr)
   180  	}
   181  
   182  	it.evt.strings.compactify()
   183  
   184  	// Convert stacks.
   185  	for id, stk := range pr.Stacks {
   186  		evt.stacks.insert(stackID(id), stack{pcs: stk})
   187  	}
   188  
   189  	// OPT(dh): if we could share the frame type between this package and
   190  	// oldtrace we wouldn't have to copy the map.
   191  	for pc, f := range pr.PCs {
   192  		evt.pcs[pc] = frame{
   193  			pc:     pc,
   194  			funcID: stringID(f.Fn),
   195  			fileID: stringID(f.File),
   196  			line:   uint64(f.Line),
   197  		}
   198  	}
   199  	pr.Stacks = nil
   200  	pr.PCs = nil
   201  	evt.stacks.compactify()
   202  	return nil
   203  }
   204  
   205  // next returns the next event, io.EOF if there are no more events, or a
   206  // descriptive error for invalid events.
   207  func (it *oldTraceConverter) next() (Event, error) {
   208  	if len(it.extra) > 0 {
   209  		ev := it.extra[0]
   210  		it.extra = it.extra[1:]
   211  
   212  		if len(it.extra) == 0 {
   213  			it.extra = it.extraArr[:0]
   214  		}
   215  		// Two events aren't allowed to fall on the same timestamp in the new API,
   216  		// but this may happen when we produce EvGoStatus events
   217  		if ev.base.time <= it.lastTs {
   218  			ev.base.time = it.lastTs + 1
   219  		}
   220  		it.lastTs = ev.base.time
   221  		return ev, nil
   222  	}
   223  
   224  	oev, ok := it.events.Pop()
   225  	if !ok {
   226  		return Event{}, io.EOF
   227  	}
   228  
   229  	ev, err := it.convertEvent(oev)
   230  
   231  	if err == errSkip {
   232  		return it.next()
   233  	} else if err != nil {
   234  		return Event{}, err
   235  	}
   236  
   237  	// Two events aren't allowed to fall on the same timestamp in the new API,
   238  	// but this may happen when we produce EvGoStatus events
   239  	if ev.base.time <= it.lastTs {
   240  		ev.base.time = it.lastTs + 1
   241  	}
   242  	it.lastTs = ev.base.time
   243  	return ev, nil
   244  }
   245  
   246  var errSkip = errors.New("skip event")
   247  
   248  // convertEvent converts an event from the old trace format to zero or more
   249  // events in the new format. Most events translate 1 to 1. Some events don't
   250  // result in an event right away, in which case convertEvent returns errSkip.
   251  // Some events result in more than one new event; in this case, convertEvent
   252  // returns the first event and stores additional events in it.extra. When
   253  // encountering events that oldtrace shouldn't be able to emit, ocnvertEvent
   254  // returns a descriptive error.
   255  func (it *oldTraceConverter) convertEvent(ev *oldtrace.Event) (OUT Event, ERR error) {
   256  	var mappedType event.Type
   257  	var mappedArgs timedEventArgs
   258  	copy(mappedArgs[:], ev.Args[:])
   259  
   260  	switch ev.Type {
   261  	case oldtrace.EvGomaxprocs:
   262  		mappedType = go122.EvProcsChange
   263  		if it.preInit {
   264  			// The first EvGomaxprocs signals the end of trace initialization. At this point we've seen
   265  			// all goroutines that already existed at trace begin.
   266  			it.preInit = false
   267  			for gid := range it.createdPreInit {
   268  				// These are goroutines that already existed when tracing started but for which we
   269  				// received neither GoWaiting, GoInSyscall, or GoStart. These are goroutines that are in
   270  				// the states _Gidle or _Grunnable.
   271  				it.extra = append(it.extra, Event{
   272  					ctx: schedCtx{
   273  						// G: GoID(gid),
   274  						G: NoGoroutine,
   275  						P: NoProc,
   276  						M: NoThread,
   277  					},
   278  					table: it.evt,
   279  					base: baseEvent{
   280  						typ:  go122.EvGoStatus,
   281  						time: Time(ev.Ts),
   282  						args: timedEventArgs{uint64(gid), ^uint64(0), uint64(go122.GoRunnable)},
   283  					},
   284  				})
   285  			}
   286  			it.createdPreInit = nil
   287  			return Event{}, errSkip
   288  		}
   289  	case oldtrace.EvProcStart:
   290  		it.procMs[ProcID(ev.P)] = ThreadID(ev.Args[0])
   291  		if _, ok := it.seenProcs[ProcID(ev.P)]; ok {
   292  			mappedType = go122.EvProcStart
   293  			mappedArgs = timedEventArgs{uint64(ev.P)}
   294  		} else {
   295  			it.seenProcs[ProcID(ev.P)] = struct{}{}
   296  			mappedType = go122.EvProcStatus
   297  			mappedArgs = timedEventArgs{uint64(ev.P), uint64(go122.ProcRunning)}
   298  		}
   299  	case oldtrace.EvProcStop:
   300  		if _, ok := it.seenProcs[ProcID(ev.P)]; ok {
   301  			mappedType = go122.EvProcStop
   302  			mappedArgs = timedEventArgs{uint64(ev.P)}
   303  		} else {
   304  			it.seenProcs[ProcID(ev.P)] = struct{}{}
   305  			mappedType = go122.EvProcStatus
   306  			mappedArgs = timedEventArgs{uint64(ev.P), uint64(go122.ProcIdle)}
   307  		}
   308  	case oldtrace.EvGCStart:
   309  		mappedType = go122.EvGCBegin
   310  	case oldtrace.EvGCDone:
   311  		mappedType = go122.EvGCEnd
   312  	case oldtrace.EvSTWStart:
   313  		sid := it.builtinToStringID[sSTWUnknown+it.trace.STWReason(ev.Args[0])]
   314  		it.lastStwReason = sid
   315  		mappedType = go122.EvSTWBegin
   316  		mappedArgs = timedEventArgs{uint64(sid)}
   317  	case oldtrace.EvSTWDone:
   318  		mappedType = go122.EvSTWEnd
   319  		mappedArgs = timedEventArgs{it.lastStwReason}
   320  	case oldtrace.EvGCSweepStart:
   321  		mappedType = go122.EvGCSweepBegin
   322  	case oldtrace.EvGCSweepDone:
   323  		mappedType = go122.EvGCSweepEnd
   324  	case oldtrace.EvGoCreate:
   325  		if it.preInit {
   326  			it.createdPreInit[GoID(ev.Args[0])] = struct{}{}
   327  			return Event{}, errSkip
   328  		}
   329  		mappedType = go122.EvGoCreate
   330  	case oldtrace.EvGoStart:
   331  		if it.preInit {
   332  			mappedType = go122.EvGoStatus
   333  			mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(go122.GoRunning)}
   334  			delete(it.createdPreInit, GoID(ev.Args[0]))
   335  		} else {
   336  			mappedType = go122.EvGoStart
   337  		}
   338  	case oldtrace.EvGoStartLabel:
   339  		it.extra = []Event{{
   340  			ctx: schedCtx{
   341  				G: GoID(ev.G),
   342  				P: ProcID(ev.P),
   343  				M: it.procMs[ProcID(ev.P)],
   344  			},
   345  			table: it.evt,
   346  			base: baseEvent{
   347  				typ:  go122.EvGoLabel,
   348  				time: Time(ev.Ts),
   349  				args: timedEventArgs{ev.Args[2]},
   350  			},
   351  		}}
   352  		return Event{
   353  			ctx: schedCtx{
   354  				G: GoID(ev.G),
   355  				P: ProcID(ev.P),
   356  				M: it.procMs[ProcID(ev.P)],
   357  			},
   358  			table: it.evt,
   359  			base: baseEvent{
   360  				typ:  go122.EvGoStart,
   361  				time: Time(ev.Ts),
   362  				args: mappedArgs,
   363  			},
   364  		}, nil
   365  	case oldtrace.EvGoEnd:
   366  		mappedType = go122.EvGoDestroy
   367  	case oldtrace.EvGoStop:
   368  		mappedType = go122.EvGoBlock
   369  		mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sForever]), uint64(ev.StkID)}
   370  	case oldtrace.EvGoSched:
   371  		mappedType = go122.EvGoStop
   372  		mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sGosched]), uint64(ev.StkID)}
   373  	case oldtrace.EvGoPreempt:
   374  		mappedType = go122.EvGoStop
   375  		mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sPreempted]), uint64(ev.StkID)}
   376  	case oldtrace.EvGoSleep:
   377  		mappedType = go122.EvGoBlock
   378  		mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSleep]), uint64(ev.StkID)}
   379  	case oldtrace.EvGoBlock:
   380  		mappedType = go122.EvGoBlock
   381  		mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sEmpty]), uint64(ev.StkID)}
   382  	case oldtrace.EvGoUnblock:
   383  		mappedType = go122.EvGoUnblock
   384  	case oldtrace.EvGoBlockSend:
   385  		mappedType = go122.EvGoBlock
   386  		mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sChanSend]), uint64(ev.StkID)}
   387  	case oldtrace.EvGoBlockRecv:
   388  		mappedType = go122.EvGoBlock
   389  		mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sChanRecv]), uint64(ev.StkID)}
   390  	case oldtrace.EvGoBlockSelect:
   391  		mappedType = go122.EvGoBlock
   392  		mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSelect]), uint64(ev.StkID)}
   393  	case oldtrace.EvGoBlockSync:
   394  		mappedType = go122.EvGoBlock
   395  		mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSync]), uint64(ev.StkID)}
   396  	case oldtrace.EvGoBlockCond:
   397  		mappedType = go122.EvGoBlock
   398  		mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSyncCond]), uint64(ev.StkID)}
   399  	case oldtrace.EvGoBlockNet:
   400  		mappedType = go122.EvGoBlock
   401  		mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sNetwork]), uint64(ev.StkID)}
   402  	case oldtrace.EvGoBlockGC:
   403  		mappedType = go122.EvGoBlock
   404  		mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sMarkAssistWait]), uint64(ev.StkID)}
   405  	case oldtrace.EvGoSysCall:
   406  		// Look for the next event for the same G to determine if the syscall
   407  		// blocked.
   408  		blocked := false
   409  		it.events.All()(func(nev *oldtrace.Event) bool {
   410  			if nev.G != ev.G {
   411  				return true
   412  			}
   413  			// After an EvGoSysCall, the next event on the same G will either be
   414  			// EvGoSysBlock to denote a blocking syscall, or some other event
   415  			// (or the end of the trace) if the syscall didn't block.
   416  			if nev.Type == oldtrace.EvGoSysBlock {
   417  				blocked = true
   418  			}
   419  			return false
   420  		})
   421  		if blocked {
   422  			mappedType = go122.EvGoSyscallBegin
   423  			mappedArgs = timedEventArgs{1: uint64(ev.StkID)}
   424  		} else {
   425  			// Convert the old instantaneous syscall event to a pair of syscall
   426  			// begin and syscall end and give it the shortest possible duration,
   427  			// 1ns.
   428  			out1 := Event{
   429  				ctx: schedCtx{
   430  					G: GoID(ev.G),
   431  					P: ProcID(ev.P),
   432  					M: it.procMs[ProcID(ev.P)],
   433  				},
   434  				table: it.evt,
   435  				base: baseEvent{
   436  					typ:  go122.EvGoSyscallBegin,
   437  					time: Time(ev.Ts),
   438  					args: timedEventArgs{1: uint64(ev.StkID)},
   439  				},
   440  			}
   441  
   442  			out2 := Event{
   443  				ctx:   out1.ctx,
   444  				table: it.evt,
   445  				base: baseEvent{
   446  					typ:  go122.EvGoSyscallEnd,
   447  					time: Time(ev.Ts + 1),
   448  					args: timedEventArgs{},
   449  				},
   450  			}
   451  
   452  			it.extra = append(it.extra, out2)
   453  			return out1, nil
   454  		}
   455  
   456  	case oldtrace.EvGoSysExit:
   457  		mappedType = go122.EvGoSyscallEndBlocked
   458  	case oldtrace.EvGoSysBlock:
   459  		return Event{}, errSkip
   460  	case oldtrace.EvGoWaiting:
   461  		mappedType = go122.EvGoStatus
   462  		mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(go122.GoWaiting)}
   463  		delete(it.createdPreInit, GoID(ev.Args[0]))
   464  	case oldtrace.EvGoInSyscall:
   465  		mappedType = go122.EvGoStatus
   466  		// In the new tracer, GoStatus with GoSyscall knows what thread the
   467  		// syscall is on. In the old tracer, EvGoInSyscall doesn't contain that
   468  		// information and all we can do here is specify NoThread.
   469  		mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(go122.GoSyscall)}
   470  		delete(it.createdPreInit, GoID(ev.Args[0]))
   471  	case oldtrace.EvHeapAlloc:
   472  		mappedType = go122.EvHeapAlloc
   473  	case oldtrace.EvHeapGoal:
   474  		mappedType = go122.EvHeapGoal
   475  	case oldtrace.EvGCMarkAssistStart:
   476  		mappedType = go122.EvGCMarkAssistBegin
   477  	case oldtrace.EvGCMarkAssistDone:
   478  		mappedType = go122.EvGCMarkAssistEnd
   479  	case oldtrace.EvUserTaskCreate:
   480  		mappedType = go122.EvUserTaskBegin
   481  		parent := ev.Args[1]
   482  		if parent == 0 {
   483  			parent = uint64(NoTask)
   484  		}
   485  		mappedArgs = timedEventArgs{ev.Args[0], parent, ev.Args[2], uint64(ev.StkID)}
   486  		name, _ := it.evt.strings.get(stringID(ev.Args[2]))
   487  		it.tasks[TaskID(ev.Args[0])] = taskState{name: name, parentID: TaskID(ev.Args[1])}
   488  	case oldtrace.EvUserTaskEnd:
   489  		mappedType = go122.EvUserTaskEnd
   490  		// Event.Task expects the parent and name to be smuggled in extra args
   491  		// and as extra strings.
   492  		ts, ok := it.tasks[TaskID(ev.Args[0])]
   493  		if ok {
   494  			delete(it.tasks, TaskID(ev.Args[0]))
   495  			mappedArgs = timedEventArgs{
   496  				ev.Args[0],
   497  				ev.Args[1],
   498  				uint64(ts.parentID),
   499  				uint64(it.evt.addExtraString(ts.name)),
   500  			}
   501  		} else {
   502  			mappedArgs = timedEventArgs{ev.Args[0], ev.Args[1], uint64(NoTask), uint64(it.evt.addExtraString(""))}
   503  		}
   504  	case oldtrace.EvUserRegion:
   505  		switch ev.Args[1] {
   506  		case 0: // start
   507  			mappedType = go122.EvUserRegionBegin
   508  		case 1: // end
   509  			mappedType = go122.EvUserRegionEnd
   510  		}
   511  		mappedArgs = timedEventArgs{ev.Args[0], ev.Args[2], uint64(ev.StkID)}
   512  	case oldtrace.EvUserLog:
   513  		mappedType = go122.EvUserLog
   514  		mappedArgs = timedEventArgs{ev.Args[0], ev.Args[1], it.inlineToStringID[ev.Args[3]], uint64(ev.StkID)}
   515  	case oldtrace.EvCPUSample:
   516  		mappedType = go122.EvCPUSample
   517  		// When emitted by the Go 1.22 tracer, CPU samples have 5 arguments:
   518  		// timestamp, M, P, G, stack. However, after they get turned into Event,
   519  		// they have the arguments stack, M, P, G.
   520  		//
   521  		// In Go 1.21, CPU samples did not have Ms.
   522  		mappedArgs = timedEventArgs{uint64(ev.StkID), ^uint64(0), uint64(ev.P), ev.G}
   523  	default:
   524  		return Event{}, fmt.Errorf("unexpected event type %v", ev.Type)
   525  	}
   526  
   527  	if oldtrace.EventDescriptions[ev.Type].Stack {
   528  		if stackIDs := go122.Specs()[mappedType].StackIDs; len(stackIDs) > 0 {
   529  			mappedArgs[stackIDs[0]-1] = uint64(ev.StkID)
   530  		}
   531  	}
   532  
   533  	m := NoThread
   534  	if ev.P != -1 && ev.Type != oldtrace.EvCPUSample {
   535  		if t, ok := it.procMs[ProcID(ev.P)]; ok {
   536  			m = ThreadID(t)
   537  		}
   538  	}
   539  	if ev.Type == oldtrace.EvProcStop {
   540  		delete(it.procMs, ProcID(ev.P))
   541  	}
   542  	g := GoID(ev.G)
   543  	if g == 0 {
   544  		g = NoGoroutine
   545  	}
   546  	out := Event{
   547  		ctx: schedCtx{
   548  			G: GoID(g),
   549  			P: ProcID(ev.P),
   550  			M: m,
   551  		},
   552  		table: it.evt,
   553  		base: baseEvent{
   554  			typ:  mappedType,
   555  			time: Time(ev.Ts),
   556  			args: mappedArgs,
   557  		},
   558  	}
   559  	return out, nil
   560  }
   561  
   562  // convertOldFormat takes a fully loaded trace in the old trace format and
   563  // returns an iterator over events in the new format.
   564  func convertOldFormat(pr oldtrace.Trace) *oldTraceConverter {
   565  	it := &oldTraceConverter{}
   566  	it.init(pr)
   567  	return it
   568  }
   569  

View as plain text