# Scheduler Tracing In Go

- Published: February 23, 2015
- Author: William Kennedy
- URL: https://www.ardanlabs.com/blog/2015/02/scheduler-tracing-in-go.html

<span style="font-size: large;">Introduction</span>
<br />
One of the things I love about Go is the profiling and debug information you can generate. There is a special environmental variable named <span style="font-family: Courier New, Courier, monospace;">GODEBUG</span> that will emit debugging information about the runtime as your program executes. You can request summary and detailed information for both the garbage collector and the scheduler. What’s great is you don't need to build your program with any special switches for it to work.

<br />
<br />
In this post, I will show you how to interpret the scheduler trace information from a sample concurrent Go program. It will help if you have a basic understanding of the scheduler. I recommend reading these two posts before continuing:<br />
<br />
Concurrency, Goroutines and GOMAXPROCS<br />
<a href="https://www.ardanlabs.com/blog/2014/01/concurrency-goroutines-and-gomaxprocs.html" target="_blank"><span class="sr-only">External resource on ardanlabs.com: </span>https&#58;//www.ardanlabs.com/blog/2014/01/concurrency-goroutines-and-gomaxprocs.html</a><br />
<br />
Go Scheduler<br />
<a href="http://morsmachine.dk/go-scheduler" target="_blank"><span class="sr-only">External resource on morsmachine.dk: </span>http&#58;//morsmachine.dk/go-scheduler</a><br />
<br />
<span style="font-size: large;">The Code</span>
<br />
This is the sample program we will use to inspect and interpret the results from <span style="font-family: Courier New, Courier, monospace;">GODEBUG</span>:<br />
<br />
<span style="background-color: #eeeeee;">&nbsp; <b>Listing 1</b> &nbsp;</span>
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
01 package main<br />
02<br />
03 import (<br />
04 &nbsp; &nbsp; &quot;sync&quot;<br />
05 &nbsp; &nbsp; &quot;time&quot;<br />
06 )<br />
07<br />
08 func main() {<br />
09 &nbsp; &nbsp; var wg sync.WaitGroup<br />
10 &nbsp; &nbsp; wg.Add(10)<br />
11<br />
12 &nbsp; &nbsp; for i := 0; i &lt; 10; i++ {<br />
13 &nbsp; &nbsp; &nbsp; &nbsp; go work(&amp;wg)<br />
14 &nbsp; &nbsp; }<br />
15<br />
16 &nbsp; &nbsp; wg.Wait()<br />
17<br />
18 &nbsp; &nbsp; // Wait to see the global run queue deplete.<br />
19 &nbsp; &nbsp; time.Sleep(3 &#42; time.Second)<br />
20 }<br />
21<br />
22 func work(wg &#42;sync.WaitGroup) {<br />
23 &nbsp; &nbsp; time.Sleep(time.Second)<br />
24<br />
25 &nbsp; &nbsp; var counter int<br />
26 &nbsp; &nbsp; for i := 0; i &lt; 1e10; i++ {<br />
27 &nbsp; &nbsp; &nbsp; &nbsp; counter++<br />
28 &nbsp; &nbsp; }<br />
29<br />
30 &nbsp; &nbsp; wg.Done()<br />
31 }
</div>
<br />
The code in listing 1 is designed to be predictable against the debug information we expect to see emitted by the runtime. On line 12, a <span style="font-family: Courier New, Courier, monospace;">for</span> loop is declared to create ten goroutines. Then the <span style="font-family: Courier New, Courier, monospace;">main</span> function waits on line 16 for all the goroutines to finish their work. The work function on line 22 sleeps for one second and then increments a local variable ten billion times. Once the incrementing is done, the function calls <span style="font-family: Courier New, Courier, monospace;">Done</span> on the wait group and returns.<br />
<br />
It is a good idea to build your program with <span style="font-family: Courier New, Courier, monospace;">go build</span> first, before setting the options for <span style="font-family: Courier New, Courier, monospace;">GODEBUG</span>. This variable is picked up by the runtime, so running Go commands will produce tracing output as well. If you use <span style="font-family: Courier New, Courier, monospace;">GODEBUG</span> in conjunction with <span style="font-family: Courier New, Courier, monospace;">go run</span> for example, then you will see trace information for the build prior to your program running.<br />
<br />
Now let’s build the program so we can run it with the <span style="font-family: Courier New, Courier, monospace;">GODEBUG</span> scheduler option:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
go build example.go
</div>
<br />
<span style="font-size: large;">Scheduler Summary Trace</span>
<br />
The <span style="font-family: Courier New, Courier, monospace;">schedtrace</span> option causes the runtime to emit a single summary line about the scheduler’s state to standard error every X milliseconds. Let’s run the program, setting the <span style="font-family: Courier New, Courier, monospace;">GODEBUG</span> option at the same time:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
GOMAXPROCS=1 GODEBUG=schedtrace=1000 ./example
</div>
<br />
Once the program starts running we will see the tracing begin. The program itself does not output anything to standard out or standard error, so we can just focus on the traces. Let’s look at the first two traces that are emitted:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
<b>SCHED 0ms</b>: gomaxprocs=1 idleprocs=0 threads=2 spinningthreads=0 idlethreads=0<br />
runqueue=0 [1]<br />
<br />
<b>SCHED 1009ms</b>: gomaxprocs=1 idleprocs=0 threads=3 spinningthreads=0 idlethreads=1<br />
runqueue=0 [9]
</div>
<br />
Let’s break down what each field represents and then understand the values based on the sample program:<br />
<br />
<div style="background-color: #efefef; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
<div style="font-family: 'Courier New', Courier, monospace;">
1009ms &nbsp; &nbsp; &nbsp; &nbsp;: Time in milliseconds since the program started.</div>
<div style="font-family: 'Courier New', Courier, monospace;">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; This is the trace for the 1 second mark.</div>
<div style="font-family: 'Courier New', Courier, monospace;">
<br /></div>
<div style="font-family: 'Courier New', Courier, monospace;">
gomaxprocs=1 &nbsp;: Number of processors configured.</div>
<div style="font-family: 'Courier New', Courier, monospace;">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Only one processor is configured for this program.</div>
<div style="font-family: 'Courier New', Courier, monospace;">
<br /></div>
<span style="font-family: Arial, Helvetica, sans-serif;"><i>Advanced Note:<br />
Think of a processor in this context as a logical processor and not a physical&nbsp;processor. The scheduler</i></span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><i>runs goroutines on these logical processors which are&nbsp;bound to a physical processor via the operating</i></span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><i>system thread that is attached.&nbsp;The operating system will schedule the thread against any physical</i></span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><i>processor&nbsp;that is available.</i></span><br />
<br />
<div style="font-family: 'Courier New', Courier, monospace;">
threads=3 &nbsp; &nbsp; : Number of threads that the runtime is managing.</div>
<div style="font-family: 'Courier New', Courier, monospace;">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Three threads exist. One for the processor and 2 others</div>
<div style="font-family: 'Courier New', Courier, monospace;">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; used by the runtime.</div>
<div style="font-family: 'Courier New', Courier, monospace;">
<br /></div>
<div style="font-family: 'Courier New', Courier, monospace;">
idlethreads=1 : Number of threads that are not busy.</div>
<div style="font-family: 'Courier New', Courier, monospace;">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1 thread idle (2 threads running).</div>
<div style="font-family: 'Courier New', Courier, monospace;">
<br /></div>
<div style="font-family: 'Courier New', Courier, monospace;">
idleprocs=0 &nbsp; : Number of processors that are not busy.</div>
<div style="font-family: 'Courier New', Courier, monospace;">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 0 processors are idle (1 processor is busy).</div>
<div style="font-family: 'Courier New', Courier, monospace;">
<br /></div>
<div style="font-family: 'Courier New', Courier, monospace;">
runqueue=0 &nbsp; &nbsp;: Number of goroutines in the global run queue.</div>
<div style="font-family: 'Courier New', Courier, monospace;">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; All runnable goroutines have been moved to a local run queue.</div>
<div style="font-family: 'Courier New', Courier, monospace;">
<br /></div>
<div style="font-family: 'Courier New', Courier, monospace;">
[9] &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; : Number of goroutines in the local run queue.</div>
<div style="font-family: 'Courier New', Courier, monospace;">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 9 goroutines are waiting inside the local run queue.
</div>
</div>
<br />
The runtime is giving us a lot of great information at the summary level. When we look at the information for the trace at the 1 second mark, we can see how one goroutine is running and the other nine goroutines are waiting inside the local run queue.<br />
<br />
<span style="background-color: #eeeeee;">&nbsp; <b>Diagram 1</b> &nbsp;</span>
<br />
<div class="separator" style="clear: both; text-align: center;">

<a href="../../../images/goinggo/diagram1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;" target="_blank"><span class="sr-only">View linked image: Screen Shot</span>![Screen Shot](../../../images/goinggo/diagram1.png)</a>

</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
In diagram 1, the processor is represented by the letter P, threads by the letter M and goroutines by the letter G. We can see how the global run queue is empty based on the <span style="font-family: Courier New, Courier, monospace;">runqueue</span> value being 0. The processor is executing a goroutine based on the <span style="font-family: Courier New, Courier, monospace;">idleprocs</span> value being 0 and the remaining goroutines we created are in the local run queue based on the value of 9 being inside the brackets.<br />
<br />
How does the trace change when we have more than one processor configured? Let’s run the program again adding <span style="font-family: Courier New, Courier, monospace;">GOMAXPROCS</span> and look at the output traces that change:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
GOMAXPROCS=2 GODEBUG=schedtrace=1000 ./example<br />
<br />
<b>SCHED 0ms</b>: gomaxprocs=2 idleprocs=1 threads=2 spinningthreads=0<br />
idlethreads=0 runqueue=0 [0 0]<br />
<br />
<b>SCHED 1002ms</b>: gomaxprocs=2 idleprocs=0 threads=4 spinningthreads=1<br />
idlethreads=1 runqueue=0 [0 4]<br />
<b><br /></b>
<b>SCHED 2006ms</b>: gomaxprocs=2 idleprocs=0 threads=4 spinningthreads=0<br />
idlethreads=1 runqueue=0 [4 4]<br />
<br />
...<br />
<b><br /></b>
<b>SCHED 6024ms</b>: gomaxprocs=2 idleprocs=0 threads=4 spinningthreads=0<br />
idlethreads=1 runqueue=2 [3 3]<br />
<br />
...<br />
<b><br /></b>
<b>SCHED 10049ms</b>: gomaxprocs=2 idleprocs=0 threads=4 spinningthreads=0<br />
idlethreads=1 runqueue=4 [2 2]<br />
...<br />
<br />
<b>SCHED 13067ms</b>: gomaxprocs=2 idleprocs=0 threads=4 spinningthreads=0<br />
idlethreads=1 runqueue=6 [1 1]<br />
<br />
...<br />
<b><br /></b>
<b>SCHED 17084ms</b>: gomaxprocs=2 idleprocs=0 threads=4 spinningthreads=0<br />
idlethreads=1 runqueue=8 [0 0]<br />
<br />
...<br />
<b><br /></b>
<b>SCHED 21100ms</b>: gomaxprocs=2 idleprocs=2 threads=4 spinningthreads=0<br />
idlethreads=2 runqueue=0 [0 0]</div>
<br />
Let’s focus on the trace for the 2 second mark when we run with two processors:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
<b>SCHED 2002ms</b>: gomaxprocs=2 <b>idleprocs=0</b> threads=4 spinningthreads=0<br />
idlethreads=1&nbsp;<b>runqueue=0 [4 4]</b><br />
<br />
2002ms &nbsp; &nbsp; &nbsp; &nbsp;: This is the trace for the 2 second mark.<br />
gomaxprocs=2 &nbsp;: 2 processors are configured for this program.<br />
threads=4 &nbsp; &nbsp; : 4 threads exist. 2 for processors and 2 for the runtime.<br />
idlethreads=1 : 1 idle thread (3 threads running).<br />
idleprocs=0 &nbsp; : 0 processors are idle (2 processors busy).<br />
runqueue=0 &nbsp; &nbsp;: All runnable goroutines have been moved to a local run queue.<br />
[4 4] &nbsp; &nbsp; &nbsp; &nbsp; : 4 goroutines are waiting inside each local run queue.
</div>
<br />
<span style="background-color: #eeeeee;">&nbsp; <b>Diagram 2</b> &nbsp;</span>
<br />
<div class="separator" style="clear: both; text-align: center;">

<a href="../../../images/goinggo/diagram2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;" target="_blank"><span class="sr-only">View linked image: Screen Shot</span>![Screen Shot](../../../images/goinggo/diagram2.png)</a>

</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Let’s look at the information in diagram 2 for the trace at the 2 second mark. We can see how a goroutine is running in each processor. We can also see that eight goroutines are waiting inside of the local run queues, four goroutines inside each of the two local run queues.<br />
<br />
Things change when we get to the 6 second mark:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
<b>SCHED 6024ms</b>: gomaxprocs=2 <b>idleprocs=0</b> threads=4 spinningthreads=0<br />
idlethreads=1 <b>runqueue=2 [3 3]</b><br />
<br />
idleprocs=0 : 0 processors are idle (2 processors busy).<br />
runqueue=2 &nbsp;: 2 goroutines returned and are waiting to be terminated.<br />
[3 3] &nbsp; &nbsp; &nbsp; : 3 goroutines are waiting inside each local run queue.</div>
<br />
<span style="background-color: #eeeeee;">&nbsp; <b>Diagram 3</b> &nbsp;</span>
<br />
<div class="separator" style="clear: both; text-align: center;">

<a href="../../../images/goinggo/diagram3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;" target="_blank"><span class="sr-only">View linked image: Screen Shot</span>![Screen Shot](../../../images/goinggo/diagram3.png)</a>

</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
When we get to the 6 second mark things change. Now in diagram 3, two of the goroutines we created completed their work and have been moved to the global run queue. We still have two goroutines running, one in each of the existing processors and three are waiting in each of the respective local run queues.<br />
<br />
<i><span style="font-family: Arial, Helvetica, sans-serif;">Advanced Note:</span></i><br />
<i><span style="font-family: Arial, Helvetica, sans-serif;">In many cases, goroutines are not moved back to the global run queue prior to being terminated. This program has created a special situation because the for loop is performing logic that runs for more than 10ms and is not calling into any functions. 10ms is the scheduling quant in the scheduler. After 10ms of execution, the scheduler tries to preempt goroutines. These goroutines can’t be preempted because they do not call into any functions. In this case, once the goroutines reach the wg.Done call, the goroutines are instantly preempted and moved to the global run queue for termination.</span></i><br />
<br />
When the 17 second mark hits, we see the last two goroutines are now running:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
<b>SCHED 17084ms</b>: gomaxprocs=2 <b>idleprocs=0</b> threads=4 spinningthreads=0<br />
idlethreads=1
<b>runqueue=8 [0 0]</b><br />
<br />
idleprocs=0 : 0 processors are idle (2 processors busy).<br />
runqueue=8 &nbsp;: 8 goroutines returned and are waiting to be terminated.<br />
[0 0] &nbsp; &nbsp; &nbsp; : No goroutines are waiting inside any local run queue.
</div>
<br />
<span style="background-color: #eeeeee;">&nbsp; <b>Diagram 4</b> &nbsp;</span>
<br />
<div class="separator" style="clear: both; text-align: center;">

<a href="../../../images/goinggo/diagram4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;" target="_blank"><span class="sr-only">View linked image: Screen Shot</span>![Screen Shot](../../../images/goinggo/diagram4.png)</a>

</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
In diagram 4, we see how eight goroutines are in the global run queue and the remaining last two goroutines are running. This leaves each of the respective local run queues empty.<br />
<br />
The final trace is at the 21 second mark:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
<b>SCHED 21100ms</b>: gomaxprocs=2 <b>idleprocs=2</b> threads=4 spinningthreads=0<br />
idlethreads=2 <b>runqueue=0 [0 0]</b><br />
<br />
idleprocs=2 : 2 processors are idle (0 processors busy).<br />
runqueue=0 &nbsp;: All the goroutines that were in the queue have been terminated.<br />
[0 0] &nbsp; &nbsp; &nbsp; : No goroutines are waiting inside any local run queue.
</div>
<br />
<span style="background-color: #eeeeee;">&nbsp; <b>Diagram 5</b> &nbsp;</span>
<br />
<div class="separator" style="clear: both; text-align: center;">

<a href="../../../images/goinggo/diagram5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;" target="_blank"><span class="sr-only">View linked image: Screen Shot</span>![Screen Shot](../../../images/goinggo/diagram5.png)</a>

</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
At this point, all the goroutines have finished their work and have been terminated.<br />
<br />
<span style="font-size: large;">Detailed Scheduler Trace</span>
<br />
The scheduler summary information is very helpful, but sometimes you need an even deeper view. In those cases, we can add the <span style="font-family: Courier New, Courier, monospace;">scheddetail</span> option which will provide detailed trace information about each processor, thread and goroutine. Let’s run the program again, setting the <span style="font-family: Courier New, Courier, monospace;">GODEBUG</span> option to emit detailed trace information:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
GOMAXPROCS=2 GODEBUG=schedtrace=1000,scheddetail=1 ./example
</div>
<br />
Here is the output at the 4 second mark:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
<b>SCHED 4028ms</b>: gomaxprocs=2 idleprocs=0 threads=4 spinningthreads=0<br />
idlethreads=1 runqueue=2 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0<br />
<b>P0</b>: status=1 schedtick=10 syscalltick=0 m=3 runqsize=3 gfreecnt=0<br />
<b>P1</b>: status=1 schedtick=10 syscalltick=1 m=2 runqsize=3 gfreecnt=0<br />
<b>M3</b>: p=0 curg=4 mallocing=0 throwing=0 gcing=0 locks=0 dying=0 helpgc=0 spinning=0 blocked=0 lockedg=-1<br />
<b>M2</b>: p=1 curg=10 mallocing=0 throwing=0 gcing=0 locks=0 dying=0 helpgc=0 spinning=0 blocked=0 lockedg=-1<br />
<b>M1</b>: p=-1 curg=-1 mallocing=0 throwing=0 gcing=0 locks=1 dying=0 helpgc=0 spinning=0 blocked=0 lockedg=-1<br />
<b>M0</b>: p=-1 curg=-1 mallocing=0 throwing=0 gcing=0 locks=0 dying=0 helpgc=0 spinning=0 blocked=0 lockedg=-1<br />
<b>G1</b>: status=4(semacquire) m=-1 lockedm=-1<br />
<b>G2</b>: status=4(force gc (idle)) m=-1 lockedm=-1<br />
<b>G3</b>: status=4(GC sweep wait) m=-1 lockedm=-1<br />
<b>G4</b>: status=2(sleep) m=3 lockedm=-1<br />
<b>G5</b>: status=1(sleep) m=-1 lockedm=-1<br />
<b>G6</b>: status=1(stack growth) m=-1 lockedm=-1<br />
<b>G7</b>: status=1(sleep) m=-1 lockedm=-1<br />
<b>G8</b>: status=1(sleep) m=-1 lockedm=-1<br />
<b>G9</b>: status=1(stack growth) m=-1 lockedm=-1<br />
<b>G10</b>: status=2(sleep) m=2 lockedm=-1<br />
<b>G11</b>: status=1(sleep) m=-1 lockedm=-1<br />
<b>G12</b>: status=1(sleep) m=-1 lockedm=-1<br />
<b>G13</b>: status=1(sleep) m=-1 lockedm=-1<br />
<b>G17</b>: status=4(timer goroutine (idle)) m=-1 lockedm=-1
</div>
<br />
The summary section is similar but now we have detailed lines for the processors, threads and goroutines. Let’s start with the processors:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
<b>P0</b>: status=1 schedtick=10 syscalltick=0 m=3 runqsize=3 gfreecnt=0<br />
<br />
<b>P1</b>: status=1 schedtick=10 syscalltick=1 m=2 runqsize=3 gfreecnt=0
</div>
<br />
P’s represents a processor. Since <span style="font-family: Courier New, Courier, monospace;">GOMAXPROCS</span> is set to 2, we see two P’s listed in the trace. Next, let’s look at the threads:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
<b>M3</b>: p=0 curg=4 mallocing=0 throwing=0 gcing=0 locks=0 dying=0 helpgc=0<br />
spinning=0 blocked=0 lockedg=-1<br />
<br />
<b>M2</b>: p=1 curg=10 mallocing=0 throwing=0 gcing=0 locks=0 dying=0 helpgc=0<br />
spinning=0 blocked=0 lockedg=-1<br />
<br />
<b>M1</b>: p=-1 curg=-1 mallocing=0 throwing=0 gcing=0 locks=1 dying=0 helpgc=0<br />
spinning=0 blocked=0 lockedg=-1<br />
<br />
<b>M0</b>: p=-1 curg=-1 mallocing=0 throwing=0 gcing=0 locks=0 dying=0 helpgc=0<br />
spinning=0 blocked=0 lockedg=-1
</div>
<br />
M’s represents a thread. Since the threads value is set to 4 in the summary trace, we see four M’s listed in the detail. The detailed trace information shows which threads belong to which processors:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
<b>P0</b>: status=1 schedtick=10 syscalltick=0 <b>m=3</b> runqsize=3 gfreecnt=0<br />
<br />
<b>M3</b>: <b>p=0</b> curg=4 mallocing=0 throwing=0 gcing=0 locks=0 dying=0 helpgc=0<br />
spinning=0 blocked=0 lockedg=-1
</div>
<br />
Here we see how thread M3 is attached to processor P0. This information is in both the P and M trace records.<br />
<br />
A G represents a goroutine. At the 4 second mark we see that there are fourteen goroutines that currently exist and seventeen goroutines have been created since the program started. We know the total number of goroutines created because of the number attached to the last G listed in the trace:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
<b>G17</b>: status=4(timer goroutine (idle)) m=-1 lockedm=-1
</div>
<br />
If this program continued to create goroutines, we would see this number increase linearly. If this program was handling web requests for example, we could use this number to get a general idea of the number of requests that have been handled. This estimate would only be close if the program didn’t create any other goroutines during the handling of the request.<br />
<br />
Next, let’s look at the goroutine the <span style="font-family: Courier New, Courier, monospace;">main</span> function is running against:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
<b>G1</b>: status=4(semacquire) m=-1 lockedm=-1<br />
<br />
30 &nbsp; &nbsp; wg.Done()</div>
<br />
We can see that the goroutine for the <span style="font-family: Courier New, Courier, monospace;">main</span> function is in a status of 4, blocked on a <span style="font-family: Courier New, Courier, monospace;">semacquire</span> which represents the wait group <span style="font-family: Courier New, Courier, monospace;">Wait</span> call.<br />
<br />
To better understand the rest of the goroutines in this trace, it is helpful to know what the status numbers represent. Here is a list of status codes that are declared in the header file for the runtime:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
status: http://golang.org/src/runtime/<br />
Gidle, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 0 <br />
Grunnable, &nbsp; &nbsp; &nbsp; &nbsp;// 1 runnable and on a run queue<br />
Grunning, &nbsp; &nbsp; &nbsp; &nbsp; // 2 running<br />
Gsyscall, &nbsp; &nbsp; &nbsp; &nbsp; // 3 performing a syscall<br />
Gwaiting, &nbsp; &nbsp; &nbsp; &nbsp; // 4 waiting for the runtime<br />
Gmoribund_unused,      // 5 currently unused, but hardcoded in gdb scripts<br />
Gdead, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 6 goroutine is dead<br />
Genqueue, &nbsp; &nbsp; &nbsp; &nbsp; // 7 only the Gscanenqueue is used<br />
Gcopystack, &nbsp; &nbsp; &nbsp; // 8 in this state when newstack is moving the stack
</div>
<br />
When we look at the ten goroutines we created, we can now look at their state and better understand what each is doing:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
<b>// Goroutines running in a processor. (idleprocs=0)</b><br />
G4: status=2(sleep) <b>m=3</b> lockedm=-1 &nbsp; -- Thread M3 / Processor P0<br />
G10: status=2(sleep)<b> m=2</b> lockedm=-1 &nbsp;-- Thread M2 / Processor P1<br />
<br />
<b>// Goroutines waiting to be run on a particular processor. (runqsize=3)</b><br />
G5: status=1(sleep) m=-1 lockedm=-1<br />
G7: status=1(sleep) m=-1 lockedm=-1<br />
G8: status=1(sleep) m=-1 lockedm=-1<br />
<br />
<b>// Goroutines waiting to be run on a particular processor. (runqsize=3)</b><br />
G11: status=1(sleep) m=-1 lockedm=-1<br />
G12: status=1(sleep) m=-1 lockedm=-1<br />
G13: status=1(sleep) m=-1 lockedm=-1<br />
<br />
<b>// Goroutines waiting on the global run queue. (runqueue=2)</b><br />
G6: status=1(stack growth) m=-1 lockedm=-1<br />
G9: status=1(stack growth) m=-1 lockedm=-1</div>
<br />
With a basic understanding of the scheduler and knowing the behavior of our program, we can get a detailed view of how things are being scheduled and the state of each processor, thread and goroutine in our program.<br />
<br />
<span style="font-size: large;">Conclusion</span>:
<br />
The <span style="font-family: Courier New, Courier, monospace;">GODEBUG</span> variable is a great way to peek into the mind of the scheduler while your program runs. It can tell you a lot about how your program is behaving. If you want to learn more, start by writing some simple programs that you can use to predict the traces coming from the scheduler. Learn what to expect before trying to look at a trace for more complicated programs.
