@@ -22,7 +22,6 @@ import (
22
22
"strings"
23
23
"sync"
24
24
"sync/atomic"
25
- "syscall"
26
25
"testing"
27
26
"time"
28
27
"unsafe"
@@ -1578,211 +1577,10 @@ func TestReadMetricsFinalizers(t *testing.T) {
1578
1577
}
1579
1578
1580
1579
func TestReadMetricsSched (t * testing.T ) {
1581
- const (
1582
- notInGo = iota
1583
- runnable
1584
- running
1585
- waiting
1586
- created
1587
- threads
1588
- numSamples
1589
- )
1590
- var s [numSamples ]metrics.Sample
1591
- s [notInGo ].Name = "/sched/goroutines/not-in-go:goroutines"
1592
- s [runnable ].Name = "/sched/goroutines/runnable:goroutines"
1593
- s [running ].Name = "/sched/goroutines/running:goroutines"
1594
- s [waiting ].Name = "/sched/goroutines/waiting:goroutines"
1595
- s [created ].Name = "/sched/goroutines-created:goroutines"
1596
- s [threads ].Name = "/sched/threads/total:threads"
1597
-
1598
- logMetrics := func (t * testing.T , s []metrics.Sample ) {
1599
- for i := range s {
1600
- t .Logf ("%s: %d" , s [i ].Name , s [i ].Value .Uint64 ())
1601
- }
1602
- }
1603
-
1604
- // generalSlack is the amount of goroutines we allow ourselves to be
1605
- // off by in any given category, either due to background system
1606
- // goroutines or testing package goroutines.
1607
- const generalSlack = 4
1608
-
1609
- // waitingSlack is the max number of blocked goroutines left
1610
- // from other tests, the testing package, or system
1611
- // goroutines.
1612
- const waitingSlack = 100
1613
-
1614
- // threadsSlack is the maximum number of threads left over
1615
- // from other tests and the runtime (sysmon, the template thread, etc.)
1616
- const threadsSlack = 20
1617
-
1618
- // Make sure GC isn't running, since GC workers interfere with
1619
- // expected counts.
1620
- defer debug .SetGCPercent (debug .SetGCPercent (- 1 ))
1621
- runtime .GC ()
1622
-
1623
- check := func (t * testing.T , s * metrics.Sample , min , max uint64 ) {
1624
- val := s .Value .Uint64 ()
1625
- if val < min {
1626
- t .Errorf ("%s too low; %d < %d" , s .Name , val , min )
1627
- }
1628
- if val > max {
1629
- t .Errorf ("%s too high; %d > %d" , s .Name , val , max )
1630
- }
1631
- }
1632
- checkEq := func (t * testing.T , s * metrics.Sample , value uint64 ) {
1633
- check (t , s , value , value )
1580
+ // This test is run in a subprocess to prevent other tests from polluting the metrics.
1581
+ output := runTestProg (t , "testprog" , "SchedMetrics" )
1582
+ want := "OK\n "
1583
+ if output != want {
1584
+ t .Fatalf ("output:\n %s\n \n wanted:\n %s" , output , want )
1634
1585
}
1635
- spinUntil := func (f func () bool ) bool {
1636
- for {
1637
- if f () {
1638
- return true
1639
- }
1640
- time .Sleep (50 * time .Millisecond )
1641
- }
1642
- }
1643
-
1644
- // Check base values.
1645
- t .Run ("base" , func (t * testing.T ) {
1646
- defer runtime .GOMAXPROCS (runtime .GOMAXPROCS (1 ))
1647
- metrics .Read (s [:])
1648
- logMetrics (t , s [:])
1649
- check (t , & s [notInGo ], 0 , generalSlack )
1650
- check (t , & s [runnable ], 0 , generalSlack )
1651
- checkEq (t , & s [running ], 1 )
1652
- check (t , & s [waiting ], 0 , waitingSlack )
1653
- })
1654
-
1655
- metrics .Read (s [:])
1656
- createdAfterBase := s [created ].Value .Uint64 ()
1657
-
1658
- // Force Running count to be high. We'll use these goroutines
1659
- // for Runnable, too.
1660
- const count = 10
1661
- var ready , exit atomic.Uint32
1662
- for i := 0 ; i < count - 1 ; i ++ {
1663
- go func () {
1664
- ready .Add (1 )
1665
- for exit .Load () == 0 {
1666
- // Spin to get us and keep us running, but check
1667
- // the exit condition so we exit out early if we're
1668
- // done.
1669
- start := time .Now ()
1670
- for time .Since (start ) < 10 * time .Millisecond && exit .Load () == 0 {
1671
- }
1672
- runtime .Gosched ()
1673
- }
1674
- }()
1675
- }
1676
- for ready .Load () < count - 1 {
1677
- runtime .Gosched ()
1678
- }
1679
-
1680
- // Be careful. We've entered a dangerous state for platforms
1681
- // that do not return back to the underlying system unless all
1682
- // goroutines are blocked, like js/wasm, since we have a bunch
1683
- // of runnable goroutines all spinning. We cannot write anything
1684
- // out.
1685
- if testenv .HasParallelism () {
1686
- t .Run ("created" , func (t * testing.T ) {
1687
- metrics .Read (s [:])
1688
- logMetrics (t , s [:])
1689
- checkEq (t , & s [created ], createdAfterBase + count )
1690
- })
1691
- t .Run ("running" , func (t * testing.T ) {
1692
- defer runtime .GOMAXPROCS (runtime .GOMAXPROCS (count + 4 ))
1693
- // It can take a little bit for the scheduler to
1694
- // distribute the goroutines to Ps, so retry until
1695
- // we see the count we expect or the test times out.
1696
- spinUntil (func () bool {
1697
- metrics .Read (s [:])
1698
- return s [running ].Value .Uint64 () >= count
1699
- })
1700
- logMetrics (t , s [:])
1701
- check (t , & s [running ], count , count + 4 )
1702
- check (t , & s [threads ], count , count + 4 + threadsSlack )
1703
- })
1704
-
1705
- // Force runnable count to be high.
1706
- t .Run ("runnable" , func (t * testing.T ) {
1707
- defer runtime .GOMAXPROCS (runtime .GOMAXPROCS (1 ))
1708
- metrics .Read (s [:])
1709
- logMetrics (t , s [:])
1710
- checkEq (t , & s [running ], 1 )
1711
- check (t , & s [runnable ], count - 1 , count + generalSlack )
1712
- })
1713
-
1714
- // Done with the running/runnable goroutines.
1715
- exit .Store (1 )
1716
- } else {
1717
- // Read metrics and then exit all the other goroutines,
1718
- // so that system calls may proceed.
1719
- metrics .Read (s [:])
1720
-
1721
- // Done with the running/runnable goroutines.
1722
- exit .Store (1 )
1723
-
1724
- // Now we can check our invariants.
1725
- t .Run ("created" , func (t * testing.T ) {
1726
- // Look for count-1 goroutines because we read metrics
1727
- // *before* t.Run goroutine was created for this sub-test.
1728
- checkEq (t , & s [created ], createdAfterBase + count - 1 )
1729
- })
1730
- t .Run ("running" , func (t * testing.T ) {
1731
- logMetrics (t , s [:])
1732
- checkEq (t , & s [running ], 1 )
1733
- checkEq (t , & s [threads ], 1 )
1734
- })
1735
- t .Run ("runnable" , func (t * testing.T ) {
1736
- logMetrics (t , s [:])
1737
- check (t , & s [runnable ], count - 1 , count + generalSlack )
1738
- })
1739
- }
1740
-
1741
- // Force not-in-go count to be high. This is a little tricky since
1742
- // we try really hard not to let things block in system calls.
1743
- // We have to drop to the syscall package to do this reliably.
1744
- t .Run ("not-in-go" , func (t * testing.T ) {
1745
- // Block a bunch of goroutines on an OS pipe.
1746
- pr , pw , err := pipe ()
1747
- if err != nil {
1748
- switch runtime .GOOS {
1749
- case "js" , "wasip1" :
1750
- t .Skip ("creating pipe:" , err )
1751
- }
1752
- t .Fatal ("creating pipe:" , err )
1753
- }
1754
- for i := 0 ; i < count ; i ++ {
1755
- go syscall .Read (pr , make ([]byte , 1 ))
1756
- }
1757
-
1758
- // Let the goroutines block.
1759
- spinUntil (func () bool {
1760
- metrics .Read (s [:])
1761
- return s [notInGo ].Value .Uint64 () >= count
1762
- })
1763
- logMetrics (t , s [:])
1764
- check (t , & s [notInGo ], count , count + generalSlack )
1765
-
1766
- syscall .Close (pw )
1767
- syscall .Close (pr )
1768
- })
1769
-
1770
- t .Run ("waiting" , func (t * testing.T ) {
1771
- // Force waiting count to be high.
1772
- const waitingCount = 1000
1773
- stop := make (chan bool )
1774
- for i := 0 ; i < waitingCount ; i ++ {
1775
- go func () { <- stop }()
1776
- }
1777
-
1778
- // Let the goroutines block.
1779
- spinUntil (func () bool {
1780
- metrics .Read (s [:])
1781
- return s [waiting ].Value .Uint64 () >= waitingCount
1782
- })
1783
- logMetrics (t , s [:])
1784
- check (t , & s [waiting ], waitingCount , waitingCount + waitingSlack )
1785
-
1786
- close (stop )
1787
- })
1788
1586
}
0 commit comments