mirror of
https://github.com/golang/go.git
synced 2026-01-30 15:42:04 +03:00
Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36fcde1676 | ||
|
|
9c0a6cec5b | ||
|
|
f3529ca961 | ||
|
|
83b181c68b | ||
|
|
a68b7d324c | ||
|
|
846c00ed3d | ||
|
|
8ca58ff90b | ||
|
|
51122090e1 | ||
|
|
f758dabf52 | ||
|
|
f296b7a6f0 | ||
|
|
14c92c5f80 | ||
|
|
f4274e64aa | ||
|
|
b7eca1cf3e | ||
|
|
6143ce337f | ||
|
|
b3382665ab | ||
|
|
6936700f21 | ||
|
|
1b41fcbce5 | ||
|
|
c4b92691b0 | ||
|
|
cd65852fee | ||
|
|
768e5d1e4d | ||
|
|
96745b980c | ||
|
|
bec8e9c9bd | ||
|
|
2e44aa30f0 | ||
|
|
c8d1e4cf83 | ||
|
|
ea3a94c92e | ||
|
|
edea4a79e8 | ||
|
|
8980ff45cf | ||
|
|
cdd55a324b | ||
|
|
e6f15fab0a | ||
|
|
9d7dad18db | ||
|
|
612ef03a23 | ||
|
|
b43b463d8f | ||
|
|
21f453b848 | ||
|
|
74870669fc | ||
|
|
ca153f4db7 | ||
|
|
f75a45c4d7 | ||
|
|
ab9d037401 | ||
|
|
564c76a268 | ||
|
|
b620f6fde5 | ||
|
|
e577ba98d8 | ||
|
|
229247d33b | ||
|
|
c5125098b2 | ||
|
|
adba22a9ae | ||
|
|
fd85ff5ee0 | ||
|
|
8e804f19b6 | ||
|
|
6717d27be2 | ||
|
|
9c41c1d8dc | ||
|
|
2e08d80732 | ||
|
|
76a6adcf3a | ||
|
|
0e9f7ac7ca | ||
|
|
e6036e7da5 | ||
|
|
c54e36905b | ||
|
|
329317472f | ||
|
|
99f8de7339 | ||
|
|
3dcb516d42 | ||
|
|
20a838ab94 | ||
|
|
c49910abc3 | ||
|
|
6a7f08952e | ||
|
|
8ced42e78b | ||
|
|
f63e55b541 | ||
|
|
17acbdb357 | ||
|
|
babeec29aa | ||
|
|
b4dca6416f | ||
|
|
f5293d77a9 | ||
|
|
51534757da | ||
|
|
d898c7b544 | ||
|
|
29ccdfc853 | ||
|
|
3f0cdedfdd | ||
|
|
a068054af1 | ||
|
|
331b8661a0 |
22
CONTRIBUTORS
22
CONTRIBUTORS
@@ -26,6 +26,7 @@
|
||||
|
||||
Aamir Khan <syst3m.w0rm@gmail.com>
|
||||
Aaron Beitch <aaronb@arista.com>
|
||||
Aaron Bieber <deftly@gmail.com>
|
||||
Aaron Cannon <cannona@fireantproductions.com>
|
||||
Aaron France <aaron.l.france@gmail.com>
|
||||
Aaron Jacobs <jacobsa@google.com>
|
||||
@@ -48,6 +49,7 @@ Adam Shannon <adamkshannon@gmail.com>
|
||||
Adam Shelton <aashelt90@gmail.com>
|
||||
Adam Sindelar <adamsh@google.com>
|
||||
Adam Thomason <athomason@gmail.com>
|
||||
Adam Williams <pwnfactory@gmail.com>
|
||||
Adam Woodbeck <adam@woodbeck.net>
|
||||
Adarsh Ravichandran <adarshravichandran91@gmail.com>
|
||||
Aditya Harindar <aditya.harindar@gmail.com>
|
||||
@@ -276,6 +278,7 @@ Awn Umar <awn@cryptolosophy.io>
|
||||
Axel Wagner <axel.wagner.hh@googlemail.com>
|
||||
Ayan George <ayan@ayan.net>
|
||||
Ayanamist Yang <ayanamist@gmail.com>
|
||||
Ayke van Laethem <aykevanlaethem@gmail.com>
|
||||
Aymerick Jéhanne <aymerick@jehanne.org>
|
||||
Azat Kaumov <kaumov.a.r@gmail.com>
|
||||
Baiju Muthukadan <baiju.m.mail@gmail.com>
|
||||
@@ -338,6 +341,7 @@ Brad Jones <rbjones@google.com>
|
||||
Brad Morgan <brad@morgabra.com>
|
||||
Brad Whitaker <bwhitaker@fastly.com>
|
||||
Braden Bassingthwaite <bbassingthwaite@vendasta.com>
|
||||
Bradford Lamson-Scribner <brad.lamson@gmail.com>
|
||||
Bradley Falzon <brad@teambrad.net>
|
||||
Brady Catherman <brady@gmail.com>
|
||||
Brady Sullivan <brady@bsull.com>
|
||||
@@ -351,6 +355,7 @@ Brett Cannon <bcannon@gmail.com>
|
||||
Brett Merrill <brett.j.merrill94@gmail.com>
|
||||
Brian Dellisanti <briandellisanti@gmail.com>
|
||||
Brian Downs <brian.downs@gmail.com>
|
||||
Brian Falk <falk@logicparty.org>
|
||||
Brian G. Merrell <bgmerrell@gmail.com>
|
||||
Brian Gitonga Marete <marete@toshnix.com> <bgmarete@gmail.com> <bgm@google.com>
|
||||
Brian Kennedy <btkennedy@gmail.com>
|
||||
@@ -404,6 +409,7 @@ Charles L. Dorian <cldorian@gmail.com>
|
||||
Charles Lee <zombie.fml@gmail.com>
|
||||
Charles Weill <weill@google.com>
|
||||
Chauncy Cullitan <chauncyc@google.com>
|
||||
Chen Zhihan <energiehund@gmail.com>
|
||||
Cherry Zhang <cherryyz@google.com>
|
||||
Chew Choon Keat <choonkeat@gmail.com>
|
||||
Cholerae Hu <choleraehyq@gmail.com>
|
||||
@@ -442,6 +448,7 @@ Christopher Cahoon <chris.cahoon@gmail.com>
|
||||
Christopher Guiney <chris@guiney.net>
|
||||
Christopher Henderson <chris@chenderson.org>
|
||||
Christopher Koch <chrisko@google.com>
|
||||
Christopher Loessl <cloessl+github@gmail.com>
|
||||
Christopher Nelson <nadiasvertex@gmail.com>
|
||||
Christopher Nielsen <m4dh4tt3r@gmail.com>
|
||||
Christopher Redden <christopher.redden@gmail.com>
|
||||
@@ -739,12 +746,14 @@ Frank Somers <fsomers@arista.com>
|
||||
Frederic Guillot <frederic.guillot@gmail.com>
|
||||
Frederick Kelly Mayle III <frederickmayle@gmail.com>
|
||||
Frederik Ring <frederik.ring@gmail.com>
|
||||
Frederik Zipp <fzipp@gmx.de>
|
||||
Fredrik Enestad <fredrik.enestad@soundtrackyourbrand.com>
|
||||
Fredrik Forsmo <fredrik.forsmo@gmail.com>
|
||||
Fredrik Wallgren <fredrik.wallgren@gmail.com>
|
||||
Frew Schmidt <github@frew.co>
|
||||
Frithjof Schulze <schulze@math.uni-hannover.de> <sfrithjof@gmail.com>
|
||||
Frits van Bommel <fvbommel@gmail.com>
|
||||
Fujimoto Kyosuke <kyoro.f@gmail.com>
|
||||
Fumitoshi Ukai <ukai@google.com>
|
||||
G. Hussain Chinoy <ghchinoy@gmail.com>
|
||||
Gaal Yahas <gaal@google.com>
|
||||
@@ -803,6 +812,7 @@ GitHub User @frennkie (6499251) <mail@rhab.de>
|
||||
GitHub User @hengwu0 (41297446) <41297446+hengwu0@users.noreply.github.com>
|
||||
GitHub User @itchyny (375258) <itchyny@hatena.ne.jp>
|
||||
GitHub User @jinmiaoluo (39730824) <jinmiaoluo@icloud.com>
|
||||
GitHub User @jopbrown (6345470) <msshane2008@gmail.com>
|
||||
GitHub User @kazyshr (30496953) <kazyshr0301@gmail.com>
|
||||
GitHub User @kc1212 (1093806) <kc1212@users.noreply.github.com>
|
||||
GitHub User @Kropekk (13366453) <kamilkropiewnicki@gmail.com>
|
||||
@@ -828,6 +838,7 @@ GitHub User @uhei (2116845) <uhei@users.noreply.github.com>
|
||||
GitHub User @uropek (39370426) <uropek@gmail.com>
|
||||
GitHub User @utkarsh-extc (53217283) <utkarsh.extc@gmail.com>
|
||||
GitHub User @witchard (4994659) <witchard@hotmail.co.uk>
|
||||
GitHub User @yah01 (12216890) <kagaminehuan@gmail.com>
|
||||
GitHub User @yuanhh (1298735) <yuan415030@gmail.com>
|
||||
GitHub User @zikaeroh (48577114) <zikaeroh@gmail.com>
|
||||
GitHub User @ZZMarquis (7624583) <zhonglingjian3821@163.com>
|
||||
@@ -897,6 +908,7 @@ Heschi Kreinick <heschi@google.com>
|
||||
Hidetatsu Yaginuma <ygnmhdtt@gmail.com>
|
||||
Hilko Bengen <bengen@hilluzination.de>
|
||||
Hiroaki Nakamura <hnakamur@gmail.com>
|
||||
Hiromichi Ema <ema.hiro@gmail.com>
|
||||
Hironao OTSUBO <motemen@gmail.com>
|
||||
Hiroshi Ioka <hirochachacha@gmail.com>
|
||||
Hitoshi Mitake <mitake.hitoshi@gmail.com>
|
||||
@@ -973,6 +985,7 @@ Jakob Borg <jakob@nym.se>
|
||||
Jakob Weisblat <jakobw@mit.edu>
|
||||
Jakub Čajka <jcajka@redhat.com>
|
||||
Jakub Ryszard Czarnowicz <j.czarnowicz@gmail.com>
|
||||
Jamal Carvalho <jamal.a.carvalho@gmail.com>
|
||||
James Aguilar <jaguilar@google.com>
|
||||
James Bardin <j.bardin@gmail.com>
|
||||
James Chacon <jchacon@google.com>
|
||||
@@ -1020,6 +1033,7 @@ Jannis Andrija Schnitzer <jannis@schnitzer.im>
|
||||
Jared Culp <jculp14@gmail.com>
|
||||
Jaroslavas Počepko <jp@webmaster.ms>
|
||||
Jason A. Donenfeld <Jason@zx2c4.com>
|
||||
Jason Baker <jason-baker@users.noreply.github.com>
|
||||
Jason Barnett <jason.w.barnett@gmail.com>
|
||||
Jason Buberel <jbuberel@google.com>
|
||||
Jason Chu <jasonchujc@gmail.com>
|
||||
@@ -1213,6 +1227,7 @@ Kamil Chmielewski <kamil.chm@gmail.com>
|
||||
Kamil Kisiel <kamil@kamilkisiel.net> <kamil.kisiel@gmail.com>
|
||||
Kamil Rytarowski <krytarowski@users.noreply.github.com>
|
||||
Kang Hu <hukangustc@gmail.com>
|
||||
Kanta Ebihara <kantaebihara@gmail.com>
|
||||
Karan Dhiman <karandhi@ca.ibm.com>
|
||||
Karel Pazdera <pazderak@gmail.com>
|
||||
Karoly Negyesi <chx1975@gmail.com>
|
||||
@@ -1252,6 +1267,7 @@ Ketan Parmar <ketanbparmar@gmail.com>
|
||||
Kevan Swanberg <kevswanberg@gmail.com>
|
||||
Kevin Ballard <kevin@sb.org>
|
||||
Kevin Burke <kev@inburke.com>
|
||||
Kévin Dunglas <dunglas@gmail.com>
|
||||
Kevin Gillette <extemporalgenome@gmail.com>
|
||||
Kevin Kirsche <kev.kirsche@gmail.com>
|
||||
Kevin Klues <klueska@gmail.com> <klueska@google.com>
|
||||
@@ -1265,6 +1281,7 @@ Kim Yongbin <kybinz@gmail.com>
|
||||
Kir Kolyshkin <kolyshkin@gmail.com>
|
||||
Kirill Motkov <Motkov.Kirill@gmail.com>
|
||||
Kirill Smelkov <kirr@nexedi.com>
|
||||
Kirill Tatchihin <kirabsuir@gmail.com>
|
||||
Kirk Han <kirk91.han@gmail.com>
|
||||
Kirklin McDonald <kirklin.mcdonald@gmail.com>
|
||||
Klaus Post <klauspost@gmail.com>
|
||||
@@ -1378,6 +1395,7 @@ Marcelo E. Magallon <marcelo.magallon@gmail.com>
|
||||
Marco Hennings <marco.hennings@freiheit.com>
|
||||
Marcus Willock <crazcalm@gmail.com>
|
||||
Marga Manterola <marga@google.com>
|
||||
Mariano Cano <mariano@smallstep.com>
|
||||
Marin Bašić <marin.basic02@gmail.com>
|
||||
Mario Arranz <marioarranzr@gmail.com>
|
||||
Marius A. Eriksen <marius@grailbio.com>
|
||||
@@ -1949,6 +1967,7 @@ Sergey 'SnakE' Gromov <snake.scaly@gmail.com>
|
||||
Sergey Arseev <sergey.arseev@intel.com>
|
||||
Sergey Dobrodey <sergey.dobrodey@synesis.ru>
|
||||
Sergey Frolov <sfrolov@google.com>
|
||||
Sergey Ivanov <ser1325@gmail.com>
|
||||
Sergey Lukjanov <me@slukjanov.name>
|
||||
Sergey Mishin <sergeymishine@gmail.com>
|
||||
Sergey Mudrik <sergey.mudrik@gmail.com>
|
||||
@@ -2090,6 +2109,7 @@ Thomas Desrosiers <thomasdesr@gmail.com>
|
||||
Thomas Habets <habets@google.com>
|
||||
Thomas Kappler <tkappler@gmail.com>
|
||||
Thomas Meson <zllak@hycik.org>
|
||||
Thomas Symborski <thomas.symborski@gmail.com>
|
||||
Thomas Wanielista <tomwans@gmail.com>
|
||||
Thorben Krueger <thorben.krueger@gmail.com>
|
||||
Thordur Bjornsson <thorduri@secnorth.net>
|
||||
@@ -2130,6 +2150,7 @@ Tom Thorogood <me+google@tomthorogood.co.uk>
|
||||
Tom Wilkie <tom@weave.works>
|
||||
Tomas Dabasinskas <tomas@dabasinskas.net>
|
||||
Tommy Schaefer <tommy.schaefer@teecom.com>
|
||||
Tomohiro Kusumoto <zabio1192@gmail.com>
|
||||
Tomoya Ishizaki <zaq1tomo@gmail.com>
|
||||
Tonis Tiigi <tonistiigi@gmail.com>
|
||||
Tony Reix <tony.reix@bull.net>
|
||||
@@ -2240,6 +2261,7 @@ Xi Ruoyao <xry23333@gmail.com>
|
||||
Xia Bin <snyh@snyh.org>
|
||||
Xiangdong Ji <xiangdong.ji@arm.com>
|
||||
Xing Xing <mikespook@gmail.com>
|
||||
Xingqang Bai <bxq2011hust@qq.com>
|
||||
Xu Fei <badgangkiller@gmail.com>
|
||||
Xudong Zhang <felixmelon@gmail.com>
|
||||
Xudong Zheng <7pkvm5aw@slicealias.com>
|
||||
|
||||
@@ -395,3 +395,14 @@ func (w *Watchdog) Start() {
|
||||
The cost of race detection varies by program, but for a typical program, memory
|
||||
usage may increase by 5-10x and execution time by 2-20x.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The race detector currently allocates an extra 8 bytes per <code>defer</code>
|
||||
and <code>recover</code> statement. Those extra allocations <a
|
||||
href="https://golang.org/issue/26813">are not recovered until the goroutine
|
||||
exits</a>. This means that if you have a long-running goroutine that is
|
||||
periodically issuing <code>defer</code> and <code>recover</code> calls,
|
||||
the program memory usage may grow without bound. These memory allocations
|
||||
will not show up in the output of <code>runtime.ReadMemStats</code> or
|
||||
<code>runtime/pprof</code>.
|
||||
</p>
|
||||
|
||||
@@ -34,6 +34,7 @@ We encourage all Go users to subscribe to
|
||||
<p>A <a href="/doc/devel/release.html">summary</a> of the changes between Go releases. Notes for the major releases:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="/doc/go1.14">Go 1.14</a> <small>(February 2020)</small></li>
|
||||
<li><a href="/doc/go1.13">Go 1.13</a> <small>(September 2019)</small></li>
|
||||
<li><a href="/doc/go1.12">Go 1.12</a> <small>(February 2019)</small></li>
|
||||
<li><a href="/doc/go1.11">Go 1.11</a> <small>(August 2018)</small></li>
|
||||
|
||||
@@ -14,24 +14,24 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
main ul li { margin: 0.5em 0; }
|
||||
</style>
|
||||
|
||||
<h2 id="introduction">DRAFT RELEASE NOTES — Introduction to Go 1.14</h2>
|
||||
<h2 id="introduction">Introduction to Go 1.14</h2>
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
Go 1.14 is not yet released. These are work-in-progress
|
||||
release notes. Go 1.14 is expected to be released in February 2020.
|
||||
</strong>
|
||||
The latest Go release, version 1.14, arrives six months after <a href="go1.13">Go 1.13</a>.
|
||||
Most of its changes are in the implementation of the toolchain, runtime, and libraries.
|
||||
As always, the release maintains the Go 1 <a href="/doc/go1compat.html">promise of compatibility</a>.
|
||||
We expect almost all Go programs to continue to compile and run as before.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Module support in the <code>go</code> command is now ready for production use,
|
||||
and we encourage all users to migrate to Go modules for dependency management.
|
||||
If you are unable to migrate due to a problem in the Go toolchain,
|
||||
please ensure that the problem has an
|
||||
<a href="https://golang.org/issue?q=is%3Aissue+is%3Aopen+label%3Amodules">open issue</a>
|
||||
filed. (If the issue is not on the <code>Go1.15</code> milestone, please let us
|
||||
know why it prevents you from migrating so that we can prioritize it
|
||||
appropriately.)
|
||||
Module support in the <code>go</code> command is now ready for production use,
|
||||
and we encourage all users to <a href="https://blog.golang.org/migrating-to-go-modules">migrate to Go
|
||||
modules for dependency management</a>. If you are unable to migrate due to a problem in the Go
|
||||
toolchain, please ensure that the problem has an
|
||||
<a href="https://golang.org/issue?q=is%3Aissue+is%3Aopen+label%3Amodules">open issue</a>
|
||||
filed. (If the issue is not on the <code>Go1.15</code> milestone, please let us
|
||||
know why it prevents you from migrating so that we can prioritize it
|
||||
appropriately.)
|
||||
</p>
|
||||
|
||||
<h2 id="language">Changes to the language</h2>
|
||||
@@ -77,6 +77,18 @@ appropriately.)
|
||||
(Data Execution Prevention)</a> enabled.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 202439 -->
|
||||
On Windows, creating a file
|
||||
via <a href="/pkg/os#CreateFile"><code>os.OpenFile</code></a> with
|
||||
the <a href="/pkg/os/#O_CREATE"><code>os.O_CREATE</code></a> flag, or
|
||||
via <a href="/pkg/syscall#Open"><code>syscall.Open</code></a> with
|
||||
the <a href="/pkg/syscall#O_CREAT"><code>syscall.O_CREAT</code></a>
|
||||
flag, will now create the file as read-only if the
|
||||
bit <code>0o200</code> (owner write permission) is not set in the
|
||||
permission argument. This makes the behavior on Windows more like
|
||||
that on Unix systems.
|
||||
</p>
|
||||
|
||||
<h3 id="wasm">WebAssembly</h3>
|
||||
|
||||
<p><!-- CL 203600 -->
|
||||
@@ -108,7 +120,7 @@ appropriately.)
|
||||
<h3 id="freebsd">FreeBSD</h3>
|
||||
|
||||
<p><!-- CL 199919 -->
|
||||
Go now supports the 64-bit ARM architecture on FreeBSD (the
|
||||
Go now supports the 64-bit ARM architecture on FreeBSD 12.0 or later (the
|
||||
<code>freebsd/arm64</code> port).
|
||||
</p>
|
||||
|
||||
@@ -393,7 +405,7 @@ appropriately.)
|
||||
<p><!-- CL 202117 -->
|
||||
This release includes experimental support for compiler-inserted
|
||||
coverage instrumentation for fuzzing.
|
||||
See <a href="https://golang.org/issue/14565">the issue</a> for more
|
||||
See <a href="https://golang.org/issue/14565">issue 14565</a> for more
|
||||
details.
|
||||
This API may change in future releases.
|
||||
</p>
|
||||
@@ -582,6 +594,13 @@ appropriately.)
|
||||
was never a documented feature. For proper escaping, see <a
|
||||
href="/pkg/encoding/json/#HTMLEscape"><code>HTMLEscape</code></a>.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 195045 -->
|
||||
<a href="/pkg/encoding/json/#Number"><code>Number</code></a> no longer
|
||||
accepts invalid numbers, to follow the documented behavior more closely.
|
||||
If a program needs to accept invalid numbers like the empty string,
|
||||
consider wrapping the type with <a href="/pkg/encoding/json/#Unmarshaler"><code>Unmarshaler</code></a>.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- encoding/json -->
|
||||
|
||||
@@ -752,6 +771,19 @@ appropriately.)
|
||||
</dd>
|
||||
</dl><!-- net/textproto -->
|
||||
|
||||
<dl id="net/url"><dt><a href="/pkg/net/url/">net/url</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 185117 -->
|
||||
When parsing of a URL fails
|
||||
(for example by <a href="/pkg/net/url/#Parse"><code>Parse</code></a>
|
||||
or <a href="/pkg/net/url/#ParseRequestURI"><code>ParseRequestURI</code></a>),
|
||||
the resulting <a href="/pkg/net/url/#Error.Error"><code>Error</code></a> message
|
||||
will now quote the unparsable URL.
|
||||
This provides clearer structure and consistency with other parsing errors.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- net/url -->
|
||||
|
||||
<dl id="os/signal"><dt><a href="/pkg/os/signal/">os/signal</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 187739 -->
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<p>
|
||||
<a href="/dl/" target="_blank">Official binary
|
||||
distributions</a> are available for the FreeBSD (release 10-STABLE and above),
|
||||
Linux, macOS (10.10 and above), and Windows operating systems and
|
||||
Linux, macOS (10.11 and above), and Windows operating systems and
|
||||
the 32-bit (<code>386</code>) and 64-bit (<code>amd64</code>) x86 processor
|
||||
architectures.
|
||||
</p>
|
||||
@@ -49,7 +49,7 @@ If your OS or architecture is not on the list, you may be able to
|
||||
<tr><td colspan="3"><hr></td></tr>
|
||||
<tr><td>FreeBSD 10.3 or later</td> <td>amd64, 386</td> <td>Debian GNU/kFreeBSD not supported</td></tr>
|
||||
<tr valign='top'><td>Linux 2.6.23 or later with glibc</td> <td>amd64, 386, arm, arm64,<br>s390x, ppc64le</td> <td>CentOS/RHEL 5.x not supported.<br>Install from source for other libc.</td></tr>
|
||||
<tr><td>macOS 10.10 or later</td> <td>amd64</td> <td>use the clang or gcc<sup>†</sup> that comes with Xcode<sup>‡</sup> for <code>cgo</code> support</td></tr>
|
||||
<tr><td>macOS 10.11 or later</td> <td>amd64</td> <td>use the clang or gcc<sup>†</sup> that comes with Xcode<sup>‡</sup> for <code>cgo</code> support</td></tr>
|
||||
<tr valign='top'><td>Windows 7, Server 2008R2 or later</td> <td>amd64, 386</td> <td>use MinGW (<code>386</code>) or MinGW-W64 (<code>amd64</code>) gcc<sup>†</sup>.<br>No need for cygwin or msys.</td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -124,6 +124,11 @@ typedef struct {
|
||||
} Issue31891B;
|
||||
|
||||
void callIssue31891(void);
|
||||
|
||||
typedef struct {
|
||||
int i;
|
||||
} Issue38408, *PIssue38408;
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -552,3 +557,8 @@ func useIssue31891B(c *C.Issue31891B) {}
|
||||
func test31891(t *testing.T) {
|
||||
C.callIssue31891()
|
||||
}
|
||||
|
||||
// issue 38408
|
||||
// A typedef pointer can be used as the element type.
|
||||
// No runtime test; just make sure it compiles.
|
||||
var _ C.PIssue38408 = &C.Issue38408{i: 1}
|
||||
|
||||
33
misc/cgo/testgodefs/testdata/issue37479.go
vendored
Normal file
33
misc/cgo/testgodefs/testdata/issue37479.go
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
/*
|
||||
typedef struct A A;
|
||||
|
||||
typedef struct {
|
||||
struct A *next;
|
||||
struct A **prev;
|
||||
} N;
|
||||
|
||||
struct A
|
||||
{
|
||||
N n;
|
||||
};
|
||||
|
||||
typedef struct B
|
||||
{
|
||||
A* a;
|
||||
} B;
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type N C.N
|
||||
|
||||
type A C.A
|
||||
|
||||
type B C.B
|
||||
23
misc/cgo/testgodefs/testdata/issue37621.go
vendored
Normal file
23
misc/cgo/testgodefs/testdata/issue37621.go
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
/*
|
||||
struct tt {
|
||||
long long a;
|
||||
long long b;
|
||||
};
|
||||
|
||||
struct s {
|
||||
struct tt ts[3];
|
||||
};
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type TT C.struct_tt
|
||||
|
||||
type S C.struct_s
|
||||
8
misc/cgo/testgodefs/testdata/main.go
vendored
8
misc/cgo/testgodefs/testdata/main.go
vendored
@@ -11,5 +11,13 @@ var v2 = v1.L
|
||||
// Test that P, Q, and R all point to byte.
|
||||
var v3 = Issue8478{P: (*byte)(nil), Q: (**byte)(nil), R: (***byte)(nil)}
|
||||
|
||||
// Test that N, A and B are fully defined
|
||||
var v4 = N{}
|
||||
var v5 = A{}
|
||||
var v6 = B{}
|
||||
|
||||
// Test that S is fully defined
|
||||
var v7 = S{}
|
||||
|
||||
func main() {
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ var filePrefixes = []string{
|
||||
"anonunion",
|
||||
"issue8478",
|
||||
"fieldtypedef",
|
||||
"issue37479",
|
||||
"issue37621",
|
||||
}
|
||||
|
||||
func TestGoDefs(t *testing.T) {
|
||||
|
||||
@@ -1,17 +1,41 @@
|
||||
This directory contains helper file for trace viewer (`go tool trace`).
|
||||
## Resources for Go's trace viewer
|
||||
|
||||
`trace_viewer_full.html` was generated by following
|
||||
[instructions](https://github.com/catapult-project/catapult/blob/master/tracing/docs/embedding-trace-viewer.md)
|
||||
on revision `dc970d3e1f7b3da5a2849de70ff253acdb70148f`
|
||||
of [catapult](https://github.com/catapult-project/catapult) using:
|
||||
Go execution trace UI (`go tool trace`) embeds
|
||||
Chrome's trace viewer (Catapult) following the
|
||||
[instructions](
|
||||
https://chromium.googlesource.com/catapult/+/refs/heads/master/tracing/docs/embedding-trace-viewer.md). This directory contains
|
||||
the helper files to embed Chrome's trace viewer.
|
||||
|
||||
The current resources were generated/copied from
|
||||
[`Catapult@9508452e18f130c98499cb4c4f1e1efaedee8962`](
|
||||
https://chromium.googlesource.com/catapult/+/9508452e18f130c98499cb4c4f1e1efaedee8962).
|
||||
|
||||
### Updating `trace_viewer_full.html`
|
||||
|
||||
The file was generated by catapult's `vulcanize_trace_viewer` command.
|
||||
```
|
||||
catapult$ ./tracing/bin/vulcanize_trace_viewer --config=full
|
||||
catapult$ cp tracing/bin/trace_viewer_full.html $GOROOT/misc/trace/trace_viewer_lean.html
|
||||
$ git clone https://chromium.googlesource.com/catapult
|
||||
$ cd catapult
|
||||
$ ./tracing/bin/vulcanize_trace_viewer --config=full
|
||||
$ cp tracing/bin/trace_viewer_full.html $GOROOT/misc/trace/trace_viewer_full.html
|
||||
```
|
||||
|
||||
We are supposed to use --config=lean (produces smaller html),
|
||||
but it is broken at the moment:
|
||||
https://github.com/catapult-project/catapult/issues/2247
|
||||
|
||||
### Updating `webcomponents.min.js`
|
||||
|
||||
`webcomponents.min.js` is necessary to let the trace viewer page
|
||||
to import the `trace_viewer_full.html`.
|
||||
This is copied from the catapult repo.
|
||||
|
||||
```
|
||||
$ cp third_party/polymer/components/webcomponentsjs/webcomponents.min.js $GOROOT/misc/trace/webcomponents.min.js
|
||||
```
|
||||
|
||||
## Licenses
|
||||
|
||||
The license for trace-viewer is as follows:
|
||||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
//
|
||||
@@ -40,3 +64,42 @@ The license for trace-viewer is as follows:
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The license for webcomponents.min.js is as follows:
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
||||
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
* Code distributed by Google as part of the polymer project is also
|
||||
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
// Copyright (c) 2014 The Polymer Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
File diff suppressed because one or more lines are too long
14
misc/trace/webcomponents.min.js
vendored
Normal file
14
misc/trace/webcomponents.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -2060,6 +2060,10 @@ var goIdent = make(map[string]*ast.Ident)
|
||||
// that may contain a pointer. This is used for cgo pointer checking.
|
||||
var unionWithPointer = make(map[ast.Expr]bool)
|
||||
|
||||
// anonymousStructTag provides a consistent tag for an anonymous struct.
|
||||
// The same dwarf.StructType pointer will always get the same tag.
|
||||
var anonymousStructTag = make(map[*dwarf.StructType]string)
|
||||
|
||||
func (c *typeConv) Init(ptrSize, intSize int64) {
|
||||
c.ptrSize = ptrSize
|
||||
c.intSize = intSize
|
||||
@@ -2243,7 +2247,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
||||
// Translate to zero-length array instead.
|
||||
count = 0
|
||||
}
|
||||
sub := c.loadType(dt.Type, pos, key)
|
||||
sub := c.Type(dt.Type, pos)
|
||||
t.Align = sub.Align
|
||||
t.Go = &ast.ArrayType{
|
||||
Len: c.intExpr(count),
|
||||
@@ -2388,7 +2392,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
||||
c.ptrs[key] = append(c.ptrs[key], t)
|
||||
|
||||
case *dwarf.QualType:
|
||||
t1 := c.loadType(dt.Type, pos, key)
|
||||
t1 := c.Type(dt.Type, pos)
|
||||
t.Size = t1.Size
|
||||
t.Align = t1.Align
|
||||
t.Go = t1.Go
|
||||
@@ -2408,8 +2412,12 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
||||
break
|
||||
}
|
||||
if tag == "" {
|
||||
tag = "__" + strconv.Itoa(tagGen)
|
||||
tagGen++
|
||||
tag = anonymousStructTag[dt]
|
||||
if tag == "" {
|
||||
tag = "__" + strconv.Itoa(tagGen)
|
||||
tagGen++
|
||||
anonymousStructTag[dt] = tag
|
||||
}
|
||||
} else if t.C.Empty() {
|
||||
t.C.Set(dt.Kind + " " + tag)
|
||||
}
|
||||
@@ -2472,7 +2480,13 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
|
||||
}
|
||||
name := c.Ident("_Ctype_" + dt.Name)
|
||||
goIdent[name.Name] = name
|
||||
sub := c.loadType(dt.Type, pos, key)
|
||||
akey := ""
|
||||
if c.anonymousStructTypedef(dt) {
|
||||
// only load type recursively for typedefs of anonymous
|
||||
// structs, see issues 37479 and 37621.
|
||||
akey = key
|
||||
}
|
||||
sub := c.loadType(dt.Type, pos, akey)
|
||||
if c.badPointerTypedef(dt) {
|
||||
// Treat this typedef as a uintptr.
|
||||
s := *sub
|
||||
@@ -2993,6 +3007,13 @@ func fieldPrefix(fld []*ast.Field) string {
|
||||
return prefix
|
||||
}
|
||||
|
||||
// anonymousStructTypedef reports whether dt is a C typedef for an anonymous
|
||||
// struct.
|
||||
func (c *typeConv) anonymousStructTypedef(dt *dwarf.TypedefType) bool {
|
||||
st, ok := dt.Type.(*dwarf.StructType)
|
||||
return ok && st.StructName == ""
|
||||
}
|
||||
|
||||
// badPointerTypedef reports whether t is a C typedef that should not be considered a pointer in Go.
|
||||
// A typedef is bad if C code sometimes stores non-pointers in this type.
|
||||
// TODO: Currently our best solution is to find these manually and list them as
|
||||
|
||||
@@ -186,6 +186,7 @@ func algtype1(t *types.Type) (AlgKind, *types.Type) {
|
||||
|
||||
// genhash returns a symbol which is the closure used to compute
|
||||
// the hash of a value of type t.
|
||||
// Note: the generated function must match runtime.typehash exactly.
|
||||
func genhash(t *types.Type) *obj.LSym {
|
||||
switch algtype(t) {
|
||||
default:
|
||||
|
||||
@@ -542,7 +542,7 @@ func methtype(t *types.Type) *types.Type {
|
||||
// Is type src assignment compatible to type dst?
|
||||
// If so, return op code to use in conversion.
|
||||
// If not, return 0.
|
||||
func assignop(src *types.Type, dst *types.Type, why *string) Op {
|
||||
func assignop(src, dst *types.Type, why *string) Op {
|
||||
if why != nil {
|
||||
*why = ""
|
||||
}
|
||||
@@ -665,7 +665,8 @@ func assignop(src *types.Type, dst *types.Type, why *string) Op {
|
||||
// Can we convert a value of type src to a value of type dst?
|
||||
// If so, return op code to use in conversion (maybe OCONVNOP).
|
||||
// If not, return 0.
|
||||
func convertop(src *types.Type, dst *types.Type, why *string) Op {
|
||||
// srcConstant indicates whether the value of type src is a constant.
|
||||
func convertop(srcConstant bool, src, dst *types.Type, why *string) Op {
|
||||
if why != nil {
|
||||
*why = ""
|
||||
}
|
||||
@@ -741,6 +742,13 @@ func convertop(src *types.Type, dst *types.Type, why *string) Op {
|
||||
return OCONV
|
||||
}
|
||||
|
||||
// Special case for constant conversions: any numeric
|
||||
// conversion is potentially okay. We'll validate further
|
||||
// within evconst. See #38117.
|
||||
if srcConstant && (src.IsInteger() || src.IsFloat() || src.IsComplex()) && (dst.IsInteger() || dst.IsFloat() || dst.IsComplex()) {
|
||||
return OCONV
|
||||
}
|
||||
|
||||
// 6. src is an integer or has type []byte or []rune
|
||||
// and dst is a string type.
|
||||
if src.IsInteger() && dst.IsString() {
|
||||
|
||||
@@ -1634,7 +1634,7 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||
return n
|
||||
}
|
||||
var why string
|
||||
n.Op = convertop(t, n.Type, &why)
|
||||
n.Op = convertop(n.Left.Op == OLITERAL, t, n.Type, &why)
|
||||
if n.Op == 0 {
|
||||
if !n.Diag() && !n.Type.Broke() && !n.Left.Diag() {
|
||||
yyerror("cannot convert %L to type %v%s", n.Left, n.Type, why)
|
||||
|
||||
@@ -977,25 +977,22 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass - deallocate any phi inputs which are now dead.
|
||||
// Second pass - deallocate all in-register phi inputs.
|
||||
for i, v := range phis {
|
||||
if !s.values[v.ID].needReg {
|
||||
continue
|
||||
}
|
||||
a := v.Args[idx]
|
||||
if !regValLiveSet.contains(a.ID) {
|
||||
// Input is dead beyond the phi, deallocate
|
||||
// anywhere else it might live.
|
||||
s.freeRegs(s.values[a.ID].regs)
|
||||
} else {
|
||||
// Input is still live.
|
||||
r := phiRegs[i]
|
||||
if r == noRegister {
|
||||
continue
|
||||
}
|
||||
if regValLiveSet.contains(a.ID) {
|
||||
// Input value is still live (it is used by something other than Phi).
|
||||
// Try to move it around before kicking out, if there is a free register.
|
||||
// We generate a Copy in the predecessor block and record it. It will be
|
||||
// deleted if never used.
|
||||
r := phiRegs[i]
|
||||
if r == noRegister {
|
||||
continue
|
||||
}
|
||||
// deleted later if never used.
|
||||
//
|
||||
// Pick a free register. At this point some registers used in the predecessor
|
||||
// block may have been deallocated. Those are the ones used for Phis. Exclude
|
||||
// them (and they are not going to be helpful anyway).
|
||||
@@ -1011,8 +1008,8 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||
s.assignReg(r2, a, c)
|
||||
s.endRegs[p.ID] = append(s.endRegs[p.ID], endReg{r2, a, c})
|
||||
}
|
||||
s.freeReg(r)
|
||||
}
|
||||
s.freeReg(r)
|
||||
}
|
||||
|
||||
// Copy phi ops into new schedule.
|
||||
@@ -1852,6 +1849,11 @@ func (s *regAllocState) shuffle(stacklive [][]ID) {
|
||||
e.process()
|
||||
}
|
||||
}
|
||||
|
||||
if s.f.pass.debug > regDebug {
|
||||
fmt.Printf("post shuffle %s\n", s.f.Name)
|
||||
fmt.Println(s.f.String())
|
||||
}
|
||||
}
|
||||
|
||||
type edgeState struct {
|
||||
|
||||
@@ -724,6 +724,40 @@ var tests = []test{
|
||||
},
|
||||
},
|
||||
|
||||
// Merging comments with -src.
|
||||
{
|
||||
"merge comments with -src A",
|
||||
[]string{"-src", p + "/merge", `A`},
|
||||
[]string{
|
||||
`A doc`,
|
||||
`func A`,
|
||||
`A comment`,
|
||||
},
|
||||
[]string{
|
||||
`Package A doc`,
|
||||
`Package B doc`,
|
||||
`B doc`,
|
||||
`B comment`,
|
||||
`B doc`,
|
||||
},
|
||||
},
|
||||
{
|
||||
"merge comments with -src B",
|
||||
[]string{"-src", p + "/merge", `B`},
|
||||
[]string{
|
||||
`B doc`,
|
||||
`func B`,
|
||||
`B comment`,
|
||||
},
|
||||
[]string{
|
||||
`Package A doc`,
|
||||
`Package B doc`,
|
||||
`A doc`,
|
||||
`A comment`,
|
||||
`A doc`,
|
||||
},
|
||||
},
|
||||
|
||||
// No dups with -u. Issue 21797.
|
||||
{
|
||||
"case matching on, no dups",
|
||||
|
||||
7
src/cmd/doc/testdata/merge/aa.go
vendored
Normal file
7
src/cmd/doc/testdata/merge/aa.go
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// Package comment A.
|
||||
package merge
|
||||
|
||||
// A doc.
|
||||
func A() {
|
||||
// A comment.
|
||||
}
|
||||
7
src/cmd/doc/testdata/merge/bb.go
vendored
Normal file
7
src/cmd/doc/testdata/merge/bb.go
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// Package comment B.
|
||||
package merge
|
||||
|
||||
// B doc.
|
||||
func B() {
|
||||
// B comment.
|
||||
}
|
||||
@@ -2506,13 +2506,21 @@
|
||||
// The "go get" command remains permitted to update go.mod even with -mod=readonly,
|
||||
// and the "go mod" commands do not take the -mod flag (or any other build flags).
|
||||
//
|
||||
// If invoked with -mod=vendor, the go command assumes that the vendor
|
||||
// directory holds the correct copies of dependencies and ignores
|
||||
// the dependency descriptions in go.mod.
|
||||
// If invoked with -mod=vendor, the go command loads packages from the main
|
||||
// module's vendor directory instead of downloading modules to and loading packages
|
||||
// from the module cache. The go command assumes the vendor directory holds
|
||||
// correct copies of dependencies, and it does not compute the set of required
|
||||
// module versions from go.mod files. However, the go command does check that
|
||||
// vendor/modules.txt (generated by 'go mod vendor') contains metadata consistent
|
||||
// with go.mod.
|
||||
//
|
||||
// If invoked with -mod=mod, the go command loads modules from the module cache
|
||||
// even if there is a vendor directory present.
|
||||
//
|
||||
// If the go command is not invoked with a -mod flag and the vendor directory
|
||||
// is present and the "go" version in go.mod is 1.14 or higher, the go command
|
||||
// will act as if it were invoked with -mod=vendor.
|
||||
//
|
||||
// Pseudo-versions
|
||||
//
|
||||
// The go.mod file and the go command more generally use semantic versions as
|
||||
@@ -2710,22 +2718,28 @@
|
||||
//
|
||||
// Modules and vendoring
|
||||
//
|
||||
// When using modules, the go command completely ignores vendor directories.
|
||||
// When using modules, the go command typically satisfies dependencies by
|
||||
// downloading modules from their sources and using those downloaded copies
|
||||
// (after verification, as described in the previous section). Vendoring may
|
||||
// be used to allow interoperation with older versions of Go, or to ensure
|
||||
// that all files used for a build are stored together in a single file tree.
|
||||
//
|
||||
// By default, the go command satisfies dependencies by downloading modules
|
||||
// from their sources and using those downloaded copies (after verification,
|
||||
// as described in the previous section). To allow interoperation with older
|
||||
// versions of Go, or to ensure that all files used for a build are stored
|
||||
// together in a single file tree, 'go mod vendor' creates a directory named
|
||||
// vendor in the root directory of the main module and stores there all the
|
||||
// packages from dependency modules that are needed to support builds and
|
||||
// tests of packages in the main module.
|
||||
// The command 'go mod vendor' constructs a directory named vendor in the main
|
||||
// module's root directory that contains copies of all packages needed to support
|
||||
// builds and tests of packages in the main module. 'go mod vendor' also
|
||||
// creates the file vendor/modules.txt that contains metadata about vendored
|
||||
// packages and module versions. This file should be kept consistent with go.mod:
|
||||
// when vendoring is used, 'go mod vendor' should be run after go.mod is updated.
|
||||
//
|
||||
// To build using the main module's top-level vendor directory to satisfy
|
||||
// dependencies (disabling use of the usual network sources and local
|
||||
// caches), use 'go build -mod=vendor'. Note that only the main module's
|
||||
// top-level vendor directory is used; vendor directories in other locations
|
||||
// are still ignored.
|
||||
// If the vendor directory is present in the main module's root directory, it will
|
||||
// be used automatically if the "go" version in the main module's go.mod file is
|
||||
// 1.14 or higher. Build commands like 'go build' and 'go test' will load packages
|
||||
// from the vendor directory instead of accessing the network or the local module
|
||||
// cache. To explicitly enable vendoring, invoke the go command with the flag
|
||||
// -mod=vendor. To disable vendoring, use the flag -mod=mod.
|
||||
//
|
||||
// Unlike vendoring in GOPATH, the go command ignores vendor directories in
|
||||
// locations other than the main module's root directory.
|
||||
//
|
||||
//
|
||||
// Module authentication using go.sum
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/str"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
@@ -438,7 +439,7 @@ func (g *Generator) exec(words []string) {
|
||||
cmd.Stderr = os.Stderr
|
||||
// Run the command in the package directory.
|
||||
cmd.Dir = g.dir
|
||||
cmd.Env = append(cfg.OrigEnv, g.env...)
|
||||
cmd.Env = str.StringList(cfg.OrigEnv, g.env)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
g.errorf("running %q: %s", words[0], err)
|
||||
|
||||
@@ -6,6 +6,7 @@ package modcmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -67,12 +68,10 @@ func verifyMod(mod module.Version) bool {
|
||||
_, zipErr = os.Stat(zip)
|
||||
}
|
||||
dir, dirErr := modfetch.DownloadDir(mod)
|
||||
if dirErr == nil {
|
||||
_, dirErr = os.Stat(dir)
|
||||
}
|
||||
data, err := ioutil.ReadFile(zip + "hash")
|
||||
if err != nil {
|
||||
if zipErr != nil && os.IsNotExist(zipErr) && dirErr != nil && os.IsNotExist(dirErr) {
|
||||
if zipErr != nil && errors.Is(zipErr, os.ErrNotExist) &&
|
||||
dirErr != nil && errors.Is(dirErr, os.ErrNotExist) {
|
||||
// Nothing downloaded yet. Nothing to verify.
|
||||
return true
|
||||
}
|
||||
@@ -81,7 +80,7 @@ func verifyMod(mod module.Version) bool {
|
||||
}
|
||||
h := string(bytes.TrimSpace(data))
|
||||
|
||||
if zipErr != nil && os.IsNotExist(zipErr) {
|
||||
if zipErr != nil && errors.Is(zipErr, os.ErrNotExist) {
|
||||
// ok
|
||||
} else {
|
||||
hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
|
||||
@@ -93,7 +92,7 @@ func verifyMod(mod module.Version) bool {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
if dirErr != nil && os.IsNotExist(dirErr) {
|
||||
if dirErr != nil && errors.Is(dirErr, os.ErrNotExist) {
|
||||
// ok
|
||||
} else {
|
||||
hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash)
|
||||
|
||||
@@ -7,6 +7,7 @@ package modfetch
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -56,8 +57,11 @@ func CachePath(m module.Version, suffix string) (string, error) {
|
||||
return filepath.Join(dir, encVer+"."+suffix), nil
|
||||
}
|
||||
|
||||
// DownloadDir returns the directory to which m should be downloaded.
|
||||
// Note that the directory may not yet exist.
|
||||
// DownloadDir returns the directory to which m should have been downloaded.
|
||||
// An error will be returned if the module path or version cannot be escaped.
|
||||
// An error satisfying errors.Is(err, os.ErrNotExist) will be returned
|
||||
// along with the directory if the directory does not exist or if the directory
|
||||
// is not completely populated.
|
||||
func DownloadDir(m module.Version) (string, error) {
|
||||
if PkgMod == "" {
|
||||
return "", fmt.Errorf("internal error: modfetch.PkgMod not set")
|
||||
@@ -76,9 +80,39 @@ func DownloadDir(m module.Version) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(PkgMod, enc+"@"+encVer), nil
|
||||
|
||||
dir := filepath.Join(PkgMod, enc+"@"+encVer)
|
||||
if fi, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
return dir, err
|
||||
} else if err != nil {
|
||||
return dir, &DownloadDirPartialError{dir, err}
|
||||
} else if !fi.IsDir() {
|
||||
return dir, &DownloadDirPartialError{dir, errors.New("not a directory")}
|
||||
}
|
||||
partialPath, err := CachePath(m, "partial")
|
||||
if err != nil {
|
||||
return dir, err
|
||||
}
|
||||
if _, err := os.Stat(partialPath); err == nil {
|
||||
return dir, &DownloadDirPartialError{dir, errors.New("not completely extracted")}
|
||||
} else if !os.IsNotExist(err) {
|
||||
return dir, err
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// DownloadDirPartialError is returned by DownloadDir if a module directory
|
||||
// exists but was not completely populated.
|
||||
//
|
||||
// DownloadDirPartialError is equivalent to os.ErrNotExist.
|
||||
type DownloadDirPartialError struct {
|
||||
Dir string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *DownloadDirPartialError) Error() string { return fmt.Sprintf("%s: %v", e.Dir, e.Err) }
|
||||
func (e *DownloadDirPartialError) Is(err error) bool { return err == os.ErrNotExist }
|
||||
|
||||
// lockVersion locks a file within the module cache that guards the downloading
|
||||
// and extraction of the zipfile for the given module version.
|
||||
func lockVersion(mod module.Version) (unlock func(), err error) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"cmd/go/internal/lockedfile"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/renameio"
|
||||
"cmd/go/internal/robustio"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/sumdb/dirhash"
|
||||
@@ -45,24 +46,27 @@ func Download(mod module.Version) (dir string, err error) {
|
||||
err error
|
||||
}
|
||||
c := downloadCache.Do(mod, func() interface{} {
|
||||
dir, err := DownloadDir(mod)
|
||||
dir, err := download(mod)
|
||||
if err != nil {
|
||||
return cached{"", err}
|
||||
}
|
||||
if err := download(mod, dir); err != nil {
|
||||
return cached{"", err}
|
||||
}
|
||||
checkMod(mod)
|
||||
return cached{dir, nil}
|
||||
}).(cached)
|
||||
return c.dir, c.err
|
||||
}
|
||||
|
||||
func download(mod module.Version, dir string) (err error) {
|
||||
// If the directory exists, the module has already been extracted.
|
||||
fi, err := os.Stat(dir)
|
||||
if err == nil && fi.IsDir() {
|
||||
return nil
|
||||
func download(mod module.Version) (dir string, err error) {
|
||||
// If the directory exists, and no .partial file exists,
|
||||
// the module has already been completely extracted.
|
||||
// .partial files may be created when future versions of cmd/go
|
||||
// extract module zip directories in place instead of extracting
|
||||
// to a random temporary directory and renaming.
|
||||
dir, err = DownloadDir(mod)
|
||||
if err == nil {
|
||||
return dir, nil
|
||||
} else if dir == "" || !errors.Is(err, os.ErrNotExist) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// To avoid cluttering the cache with extraneous files,
|
||||
@@ -70,22 +74,24 @@ func download(mod module.Version, dir string) (err error) {
|
||||
// Invoke DownloadZip before locking the file.
|
||||
zipfile, err := DownloadZip(mod)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
unlock, err := lockVersion(mod)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
// Check whether the directory was populated while we were waiting on the lock.
|
||||
fi, err = os.Stat(dir)
|
||||
if err == nil && fi.IsDir() {
|
||||
return nil
|
||||
_, dirErr := DownloadDir(mod)
|
||||
if dirErr == nil {
|
||||
return dir, nil
|
||||
}
|
||||
_, dirExists := dirErr.(*DownloadDirPartialError)
|
||||
|
||||
// Clean up any remaining temporary directories from previous runs.
|
||||
// Clean up any remaining temporary directories from previous runs, as well
|
||||
// as partially extracted diectories created by future versions of cmd/go.
|
||||
// This is only safe to do because the lock file ensures that their writers
|
||||
// are no longer active.
|
||||
parentDir := filepath.Dir(dir)
|
||||
@@ -95,6 +101,19 @@ func download(mod module.Version, dir string) (err error) {
|
||||
RemoveAll(path) // best effort
|
||||
}
|
||||
}
|
||||
if dirExists {
|
||||
if err := RemoveAll(dir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
partialPath, err := CachePath(mod, "partial")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.Remove(partialPath); err != nil && !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Extract the zip file to a temporary directory, then rename it to the
|
||||
// final path. That way, we can use the existence of the source directory to
|
||||
@@ -102,11 +121,11 @@ func download(mod module.Version, dir string) (err error) {
|
||||
// the entire directory (e.g. as an attempt to prune out file corruption)
|
||||
// the module cache will still be left in a recoverable state.
|
||||
if err := os.MkdirAll(parentDir, 0777); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
tmpDir, err := ioutil.TempDir(parentDir, tmpPrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
@@ -116,11 +135,11 @@ func download(mod module.Version, dir string) (err error) {
|
||||
|
||||
if err := modzip.Unzip(tmpDir, mod, zipfile); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "-> %s\n", err)
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := os.Rename(tmpDir, dir); err != nil {
|
||||
return err
|
||||
if err := robustio.Rename(tmpDir, dir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !cfg.ModCacheRW {
|
||||
@@ -128,7 +147,7 @@ func download(mod module.Version, dir string) (err error) {
|
||||
// os.Rename was observed to fail for read-only directories on macOS.
|
||||
makeDirsReadOnly(dir)
|
||||
}
|
||||
return nil
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
var downloadZipCache par.Cache
|
||||
|
||||
@@ -148,9 +148,7 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
|
||||
}
|
||||
dir, err := modfetch.DownloadDir(mod)
|
||||
if err == nil {
|
||||
if info, err := os.Stat(dir); err == nil && info.IsDir() {
|
||||
m.Dir = dir
|
||||
}
|
||||
m.Dir = dir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,13 +176,21 @@ not need updates, such as in a continuous integration and testing system.
|
||||
The "go get" command remains permitted to update go.mod even with -mod=readonly,
|
||||
and the "go mod" commands do not take the -mod flag (or any other build flags).
|
||||
|
||||
If invoked with -mod=vendor, the go command assumes that the vendor
|
||||
directory holds the correct copies of dependencies and ignores
|
||||
the dependency descriptions in go.mod.
|
||||
If invoked with -mod=vendor, the go command loads packages from the main
|
||||
module's vendor directory instead of downloading modules to and loading packages
|
||||
from the module cache. The go command assumes the vendor directory holds
|
||||
correct copies of dependencies, and it does not compute the set of required
|
||||
module versions from go.mod files. However, the go command does check that
|
||||
vendor/modules.txt (generated by 'go mod vendor') contains metadata consistent
|
||||
with go.mod.
|
||||
|
||||
If invoked with -mod=mod, the go command loads modules from the module cache
|
||||
even if there is a vendor directory present.
|
||||
|
||||
If the go command is not invoked with a -mod flag and the vendor directory
|
||||
is present and the "go" version in go.mod is 1.14 or higher, the go command
|
||||
will act as if it were invoked with -mod=vendor.
|
||||
|
||||
Pseudo-versions
|
||||
|
||||
The go.mod file and the go command more generally use semantic versions as
|
||||
@@ -380,22 +388,28 @@ the format of the cached downloaded packages.
|
||||
|
||||
Modules and vendoring
|
||||
|
||||
When using modules, the go command completely ignores vendor directories.
|
||||
When using modules, the go command typically satisfies dependencies by
|
||||
downloading modules from their sources and using those downloaded copies
|
||||
(after verification, as described in the previous section). Vendoring may
|
||||
be used to allow interoperation with older versions of Go, or to ensure
|
||||
that all files used for a build are stored together in a single file tree.
|
||||
|
||||
By default, the go command satisfies dependencies by downloading modules
|
||||
from their sources and using those downloaded copies (after verification,
|
||||
as described in the previous section). To allow interoperation with older
|
||||
versions of Go, or to ensure that all files used for a build are stored
|
||||
together in a single file tree, 'go mod vendor' creates a directory named
|
||||
vendor in the root directory of the main module and stores there all the
|
||||
packages from dependency modules that are needed to support builds and
|
||||
tests of packages in the main module.
|
||||
The command 'go mod vendor' constructs a directory named vendor in the main
|
||||
module's root directory that contains copies of all packages needed to support
|
||||
builds and tests of packages in the main module. 'go mod vendor' also
|
||||
creates the file vendor/modules.txt that contains metadata about vendored
|
||||
packages and module versions. This file should be kept consistent with go.mod:
|
||||
when vendoring is used, 'go mod vendor' should be run after go.mod is updated.
|
||||
|
||||
To build using the main module's top-level vendor directory to satisfy
|
||||
dependencies (disabling use of the usual network sources and local
|
||||
caches), use 'go build -mod=vendor'. Note that only the main module's
|
||||
top-level vendor directory is used; vendor directories in other locations
|
||||
are still ignored.
|
||||
If the vendor directory is present in the main module's root directory, it will
|
||||
be used automatically if the "go" version in the main module's go.mod file is
|
||||
1.14 or higher. Build commands like 'go build' and 'go test' will load packages
|
||||
from the vendor directory instead of accessing the network or the local module
|
||||
cache. To explicitly enable vendoring, invoke the go command with the flag
|
||||
-mod=vendor. To disable vendoring, use the flag -mod=mod.
|
||||
|
||||
Unlike vendoring in GOPATH, the go command ignores vendor directories in
|
||||
locations other than the main module's root directory.
|
||||
`,
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const arbitraryTimeout = 500 * time.Millisecond
|
||||
const arbitraryTimeout = 2000 * time.Millisecond
|
||||
|
||||
// retry retries ephemeral errors from f up to an arbitrary timeout
|
||||
// to work around filesystem flakiness on Windows and Darwin.
|
||||
|
||||
@@ -1142,7 +1142,7 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
|
||||
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Dir = a.Package.Dir
|
||||
cmd.Env = base.EnvForDir(cmd.Dir, cfg.OrigEnv)
|
||||
cmd.Env = base.EnvForDir(cmd.Dir, cfg.OrigEnv[:len(cfg.OrigEnv):len(cfg.OrigEnv)])
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stdout
|
||||
|
||||
@@ -1224,6 +1224,14 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
|
||||
if len(out) == 0 {
|
||||
fmt.Fprintf(cmd.Stdout, "%s\n", err)
|
||||
}
|
||||
// NOTE(golang.org/issue/37555): test2json reports that a test passes
|
||||
// unless "FAIL" is printed at the beginning of a line. The test may not
|
||||
// actually print that if it panics, exits, or terminates abnormally,
|
||||
// so we print it here. We can't always check whether it was printed
|
||||
// because some tests need stdout to be a terminal (golang.org/issue/34791),
|
||||
// not a pipe.
|
||||
// TODO(golang.org/issue/29062): tests that exit with status 0 without
|
||||
// printing a final result should fail.
|
||||
fmt.Fprintf(cmd.Stdout, "FAIL\t%s\t%s\n", a.Package.ImportPath, t)
|
||||
}
|
||||
|
||||
|
||||
@@ -213,6 +213,9 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
|
||||
} else if cfg.BuildTrimpath && p.Module != nil {
|
||||
fmt.Fprintf(h, "module %s@%s\n", p.Module.Path, p.Module.Version)
|
||||
}
|
||||
if p.Module != nil {
|
||||
fmt.Fprintf(h, "go %s\n", p.Module.GoVersion)
|
||||
}
|
||||
fmt.Fprintf(h, "goos %s goarch %s\n", cfg.Goos, cfg.Goarch)
|
||||
fmt.Fprintf(h, "import %q\n", p.ImportPath)
|
||||
fmt.Fprintf(h, "omitdebug %v standard %v local %v prefix %q\n", p.Internal.OmitDebug, p.Standard, p.Internal.Local, p.Internal.LocalPrefix)
|
||||
|
||||
54
src/cmd/go/testdata/script/mod_download_partial.txt
vendored
Normal file
54
src/cmd/go/testdata/script/mod_download_partial.txt
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
# Download a module
|
||||
go mod download -modcacherw rsc.io/quote
|
||||
exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
|
||||
|
||||
# 'go mod verify' should fail if we delete a file.
|
||||
go mod verify
|
||||
rm $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
|
||||
! go mod verify
|
||||
|
||||
# Create a .partial file to simulate an failure extracting the zip file.
|
||||
cp empty $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.partial
|
||||
|
||||
# 'go mod verify' should not fail, since the module hasn't been completely
|
||||
# ingested into the cache.
|
||||
go mod verify
|
||||
|
||||
# 'go list' should not load packages from the directory.
|
||||
# NOTE: the message "directory $dir outside available modules" is reported
|
||||
# for directories not in the main module, active modules in the module cache,
|
||||
# or local replacements. In this case, the directory is in the right place,
|
||||
# but it's incomplete, so 'go list' acts as if it's not an active module.
|
||||
! go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.2
|
||||
stderr 'outside available modules'
|
||||
|
||||
# 'go list -m' should not print the directory.
|
||||
go list -m -f '{{.Dir}}' rsc.io/quote
|
||||
! stdout .
|
||||
|
||||
# 'go mod download' should re-extract the module and remove the .partial file.
|
||||
go mod download -modcacherw rsc.io/quote
|
||||
! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.partial
|
||||
exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
|
||||
|
||||
# 'go list' should succeed.
|
||||
go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.2
|
||||
stdout '^rsc.io/quote$'
|
||||
|
||||
# 'go list -m' should print the directory.
|
||||
go list -m -f '{{.Dir}}' rsc.io/quote
|
||||
stdout 'pkg[/\\]mod[/\\]rsc.io[/\\]quote@v1.5.2'
|
||||
|
||||
# go mod verify should fail if we delete a file.
|
||||
go mod verify
|
||||
rm $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
|
||||
! go mod verify
|
||||
|
||||
-- go.mod --
|
||||
module m
|
||||
|
||||
go 1.14
|
||||
|
||||
require rsc.io/quote v1.5.2
|
||||
|
||||
-- empty --
|
||||
7
src/cmd/go/testdata/script/mod_edit_go.txt
vendored
7
src/cmd/go/testdata/script/mod_edit_go.txt
vendored
@@ -7,6 +7,13 @@ go mod edit -go=1.9
|
||||
grep 'go 1.9' go.mod
|
||||
go build
|
||||
|
||||
# Reverting the version should force a rebuild and error instead of using
|
||||
# the cached 1.9 build. (https://golang.org/issue/37804)
|
||||
go mod edit -go=1.8
|
||||
! go build
|
||||
stderr 'type aliases only supported as of'
|
||||
|
||||
|
||||
-- go.mod --
|
||||
module m
|
||||
go 1.8
|
||||
|
||||
69
src/cmd/go/testdata/script/test_json_panic_exit.txt
vendored
Normal file
69
src/cmd/go/testdata/script/test_json_panic_exit.txt
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
# Verifies golang.org/issue/37555.
|
||||
|
||||
[short] skip
|
||||
|
||||
# 'go test -json' should say a test passes if it says it passes.
|
||||
go test -json ./pass
|
||||
stdout '"Action":"pass".*\n\z'
|
||||
! stdout '"Test":.*\n\z'
|
||||
|
||||
# 'go test -json' should say a test passes if it exits 0 and prints nothing.
|
||||
# TODO(golang.org/issue/29062): this should fail in the future.
|
||||
go test -json ./exit0main
|
||||
stdout '"Action":"pass".*\n\z'
|
||||
! stdout '"Test":.*\n\z'
|
||||
|
||||
# 'go test -json' should say a test fails if it exits 1 and prints nothing.
|
||||
! go test -json ./exit1main
|
||||
stdout '"Action":"fail".*\n\z'
|
||||
! stdout '"Test":.*\n\z'
|
||||
|
||||
# 'go test -json' should say a test fails if it panics.
|
||||
! go test -json ./panic
|
||||
stdout '"Action":"fail".*\n\z'
|
||||
! stdout '"Test":.*\n\z'
|
||||
|
||||
-- go.mod --
|
||||
module example.com/test
|
||||
|
||||
go 1.14
|
||||
|
||||
-- pass/pass_test.go --
|
||||
package pass_test
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPass(t *testing.T) {}
|
||||
|
||||
-- exit0main/exit0main_test.go --
|
||||
package exit0_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
-- exit1main/exit1main_test.go --
|
||||
package exit1_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
-- panic/panic_test.go --
|
||||
package panic_test
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPanic(t *testing.T) {
|
||||
panic("oh no")
|
||||
}
|
||||
30
src/cmd/go/testdata/script/test_main_panic.txt
vendored
Normal file
30
src/cmd/go/testdata/script/test_main_panic.txt
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
[short] skip
|
||||
[!race] skip
|
||||
|
||||
! go test -v -race main_panic/testmain_parallel_sub_panic_test.go
|
||||
! stdout 'DATA RACE'
|
||||
-- main_panic/testmain_parallel_sub_panic_test.go --
|
||||
package testmain_parallel_sub_panic_test
|
||||
|
||||
import "testing"
|
||||
|
||||
func setup() { println("setup()") }
|
||||
func teardown() { println("teardown()") }
|
||||
func TestA(t *testing.T) {
|
||||
t.Run("1", func(t *testing.T) {
|
||||
t.Run("1", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
panic("A/1/1 panics")
|
||||
})
|
||||
t.Run("2", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
println("A/1/2 is ok")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
setup()
|
||||
defer teardown()
|
||||
m.Run()
|
||||
}
|
||||
@@ -128,9 +128,16 @@ func (c *converter) Write(b []byte) (int, error) {
|
||||
}
|
||||
|
||||
var (
|
||||
// printed by test on successful run.
|
||||
bigPass = []byte("PASS\n")
|
||||
|
||||
// printed by test after a normal test failure.
|
||||
bigFail = []byte("FAIL\n")
|
||||
|
||||
// printed by 'go test' along with an error if the test binary terminates
|
||||
// with an error.
|
||||
bigFailErrorPrefix = []byte("FAIL\t")
|
||||
|
||||
updates = [][]byte{
|
||||
[]byte("=== RUN "),
|
||||
[]byte("=== PAUSE "),
|
||||
@@ -155,7 +162,7 @@ var (
|
||||
// before or after emitting other events.
|
||||
func (c *converter) handleInputLine(line []byte) {
|
||||
// Final PASS or FAIL.
|
||||
if bytes.Equal(line, bigPass) || bytes.Equal(line, bigFail) {
|
||||
if bytes.Equal(line, bigPass) || bytes.Equal(line, bigFail) || bytes.HasPrefix(line, bigFailErrorPrefix) {
|
||||
c.flushReport(0)
|
||||
c.output.write(line)
|
||||
if bytes.Equal(line, bigPass) {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
{"Action":"output","Test":"TestPanic","Output":"\tgo/src/testing/testing.go:909 +0xc9\n"}
|
||||
{"Action":"output","Test":"TestPanic","Output":"created by testing.(*T).Run\n"}
|
||||
{"Action":"output","Test":"TestPanic","Output":"\tgo/src/testing/testing.go:960 +0x350\n"}
|
||||
{"Action":"output","Test":"TestPanic","Output":"FAIL\tcommand-line-arguments\t0.042s\n"}
|
||||
{"Action":"fail","Test":"TestPanic"}
|
||||
{"Action":"output","Output":"FAIL\tcommand-line-arguments\t0.042s\n"}
|
||||
{"Action":"output","Output":"FAIL\n"}
|
||||
{"Action":"fail"}
|
||||
|
||||
@@ -25,6 +25,7 @@ func init() {
|
||||
http.HandleFunc("/trace", httpTrace)
|
||||
http.HandleFunc("/jsontrace", httpJsonTrace)
|
||||
http.HandleFunc("/trace_viewer_html", httpTraceViewerHTML)
|
||||
http.HandleFunc("/webcomponents.min.js", webcomponentsJS)
|
||||
}
|
||||
|
||||
// httpTrace serves either whole trace (goid==0) or trace for goid goroutine.
|
||||
@@ -43,14 +44,26 @@ func httpTrace(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
}
|
||||
|
||||
// See https://github.com/catapult-project/catapult/blob/master/tracing/docs/embedding-trace-viewer.md
|
||||
// This is almost verbatim copy of:
|
||||
// https://github.com/catapult-project/catapult/blob/master/tracing/bin/index.html
|
||||
// on revision 5f9e4c3eaa555bdef18218a89f38c768303b7b6e.
|
||||
// https://chromium.googlesource.com/catapult/+/9508452e18f130c98499cb4c4f1e1efaedee8962/tracing/docs/embedding-trace-viewer.md
|
||||
// This is almost verbatim copy of https://chromium-review.googlesource.com/c/catapult/+/2062938/2/tracing/bin/index.html
|
||||
var templTrace = `
|
||||
<html>
|
||||
<head>
|
||||
<link href="/trace_viewer_html" rel="import">
|
||||
<script src="/webcomponents.min.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function onTraceViewerImportFail() {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.body.textContent =
|
||||
'/trace_viewer_full.html is missing. File a bug in https://golang.org/issue';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<link rel="import" href="/trace_viewer_html"
|
||||
onerror="onTraceViewerImportFail(event)">
|
||||
|
||||
<style type="text/css">
|
||||
html, body {
|
||||
box-sizing: border-box;
|
||||
@@ -77,10 +90,10 @@ var templTrace = `
|
||||
|
||||
function load() {
|
||||
var req = new XMLHttpRequest();
|
||||
var is_binary = /[.]gz$/.test(url) || /[.]zip$/.test(url);
|
||||
var isBinary = /[.]gz$/.test(url) || /[.]zip$/.test(url);
|
||||
req.overrideMimeType('text/plain; charset=x-user-defined');
|
||||
req.open('GET', url, true);
|
||||
if (is_binary)
|
||||
if (isBinary)
|
||||
req.responseType = 'arraybuffer';
|
||||
|
||||
req.onreadystatechange = function(event) {
|
||||
@@ -89,7 +102,7 @@ var templTrace = `
|
||||
|
||||
window.setTimeout(function() {
|
||||
if (req.status === 200)
|
||||
onResult(is_binary ? req.response : req.responseText);
|
||||
onResult(isBinary ? req.response : req.responseText);
|
||||
else
|
||||
onResultFail(req.status);
|
||||
}, 0);
|
||||
@@ -136,17 +149,17 @@ var templTrace = `
|
||||
overlay.visible = true;
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.addEventListener('WebComponentsReady', function() {
|
||||
var container = document.createElement('track-view-container');
|
||||
container.id = 'track_view_container';
|
||||
|
||||
viewer = document.createElement('tr-ui-timeline-view');
|
||||
viewer.track_view_container = container;
|
||||
viewer.appendChild(container);
|
||||
Polymer.dom(viewer).appendChild(container);
|
||||
|
||||
viewer.id = 'trace-viewer';
|
||||
viewer.globalMode = true;
|
||||
document.body.appendChild(viewer);
|
||||
Polymer.dom(document.body).appendChild(viewer);
|
||||
|
||||
url = '/jsontrace?{{PARAMS}}';
|
||||
load();
|
||||
@@ -165,6 +178,10 @@ func httpTraceViewerHTML(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, filepath.Join(runtime.GOROOT(), "misc", "trace", "trace_viewer_full.html"))
|
||||
}
|
||||
|
||||
func webcomponentsJS(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, filepath.Join(runtime.GOROOT(), "misc", "trace", "webcomponents.min.js"))
|
||||
}
|
||||
|
||||
// httpJsonTrace serves json trace, requested from within templTrace HTML.
|
||||
func httpJsonTrace(w http.ResponseWriter, r *http.Request) {
|
||||
defer debug.FreeOSMemory()
|
||||
|
||||
@@ -86,7 +86,8 @@ func NewGCM(cipher Block) (AEAD, error) {
|
||||
}
|
||||
|
||||
// NewGCMWithNonceSize returns the given 128-bit, block cipher wrapped in Galois
|
||||
// Counter Mode, which accepts nonces of the given length.
|
||||
// Counter Mode, which accepts nonces of the given length. The length must not
|
||||
// be zero.
|
||||
//
|
||||
// Only use this function if you require compatibility with an existing
|
||||
// cryptosystem that uses non-standard nonce lengths. All other users should use
|
||||
@@ -112,6 +113,10 @@ func newGCMWithNonceAndTagSize(cipher Block, nonceSize, tagSize int) (AEAD, erro
|
||||
return nil, errors.New("cipher: incorrect tag size given to GCM")
|
||||
}
|
||||
|
||||
if nonceSize <= 0 {
|
||||
return nil, errors.New("cipher: the nonce can't have zero length, or the security of the key will be immediately compromised")
|
||||
}
|
||||
|
||||
if cipher, ok := cipher.(gcmAble); ok {
|
||||
return cipher.NewGCM(nonceSize, tagSize)
|
||||
}
|
||||
|
||||
@@ -217,6 +217,13 @@ var aesGCMTests = []struct {
|
||||
"2b9680b886b3efb7c6354b38c63b5373",
|
||||
"e2b7e5ed5ff27fc8664148f5a628a46dcbf2015184fffb82f2651c36",
|
||||
},
|
||||
{
|
||||
"11754cd72aec309bf52f7687212e8957",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"250327c674aaf477aef2675748cf6971",
|
||||
},
|
||||
}
|
||||
|
||||
func TestAESGCM(t *testing.T) {
|
||||
@@ -234,14 +241,22 @@ func TestAESGCM(t *testing.T) {
|
||||
|
||||
var aesgcm cipher.AEAD
|
||||
switch {
|
||||
// Handle non-standard nonce sizes
|
||||
// Handle non-standard tag sizes
|
||||
case tagSize != 16:
|
||||
aesgcm, err = cipher.NewGCMWithTagSize(aes, tagSize)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Handle non-standard tag sizes
|
||||
// Handle 0 nonce size (expect error and continue)
|
||||
case len(nonce) == 0:
|
||||
aesgcm, err = cipher.NewGCMWithNonceSize(aes, 0)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for zero nonce size")
|
||||
}
|
||||
continue
|
||||
|
||||
// Handle non-standard nonce sizes
|
||||
case len(nonce) != 12:
|
||||
aesgcm, err = cipher.NewGCMWithNonceSize(aes, len(nonce))
|
||||
if err != nil {
|
||||
|
||||
@@ -88,6 +88,9 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e
|
||||
switch status {
|
||||
case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
|
||||
return CertificateInvalidError{c, Expired, ""}
|
||||
case syscall.CERT_TRUST_IS_NOT_VALID_FOR_USAGE:
|
||||
return CertificateInvalidError{c, IncompatibleUsage, ""}
|
||||
// TODO(filippo): surface more error statuses.
|
||||
default:
|
||||
return UnknownAuthorityError{c, nil, nil}
|
||||
}
|
||||
@@ -138,11 +141,19 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex
|
||||
return nil
|
||||
}
|
||||
|
||||
// windowsExtKeyUsageOIDs are the C NUL-terminated string representations of the
|
||||
// OIDs for use with the Windows API.
|
||||
var windowsExtKeyUsageOIDs = make(map[ExtKeyUsage][]byte, len(extKeyUsageOIDs))
|
||||
|
||||
func init() {
|
||||
for _, eku := range extKeyUsageOIDs {
|
||||
windowsExtKeyUsageOIDs[eku.extKeyUsage] = []byte(eku.oid.String() + "\x00")
|
||||
}
|
||||
}
|
||||
|
||||
// systemVerify is like Verify, except that it uses CryptoAPI calls
|
||||
// to build certificate chains and verify them.
|
||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||
hasDNSName := opts != nil && len(opts.DNSName) > 0
|
||||
|
||||
storeCtx, err := createStoreContext(c, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -152,17 +163,26 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
|
||||
para := new(syscall.CertChainPara)
|
||||
para.Size = uint32(unsafe.Sizeof(*para))
|
||||
|
||||
// If there's a DNSName set in opts, assume we're verifying
|
||||
// a certificate from a TLS server.
|
||||
if hasDNSName {
|
||||
oids := []*byte{
|
||||
&syscall.OID_PKIX_KP_SERVER_AUTH[0],
|
||||
// Both IE and Chrome allow certificates with
|
||||
// Server Gated Crypto as well. Some certificates
|
||||
// in the wild require them.
|
||||
&syscall.OID_SERVER_GATED_CRYPTO[0],
|
||||
&syscall.OID_SGC_NETSCAPE[0],
|
||||
keyUsages := opts.KeyUsages
|
||||
if len(keyUsages) == 0 {
|
||||
keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
|
||||
}
|
||||
oids := make([]*byte, 0, len(keyUsages))
|
||||
for _, eku := range keyUsages {
|
||||
if eku == ExtKeyUsageAny {
|
||||
oids = nil
|
||||
break
|
||||
}
|
||||
if oid, ok := windowsExtKeyUsageOIDs[eku]; ok {
|
||||
oids = append(oids, &oid[0])
|
||||
}
|
||||
// Like the standard verifier, accept SGC EKUs as equivalent to ServerAuth.
|
||||
if eku == ExtKeyUsageServerAuth {
|
||||
oids = append(oids, &syscall.OID_SERVER_GATED_CRYPTO[0])
|
||||
oids = append(oids, &syscall.OID_SGC_NETSCAPE[0])
|
||||
}
|
||||
}
|
||||
if oids != nil {
|
||||
para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
|
||||
para.RequestedUsage.Usage.Length = uint32(len(oids))
|
||||
para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
|
||||
@@ -208,7 +228,7 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if hasDNSName {
|
||||
if opts != nil && len(opts.DNSName) > 0 {
|
||||
err = checkChainSSLServerPolicy(c, chainCtx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -188,23 +188,32 @@ var errNotParsed = errors.New("x509: missing ASN.1 contents; use ParseCertificat
|
||||
// VerifyOptions contains parameters for Certificate.Verify. It's a structure
|
||||
// because other PKIX verification APIs have ended up needing many options.
|
||||
type VerifyOptions struct {
|
||||
DNSName string
|
||||
// DNSName, if set, is checked against the leaf certificate with
|
||||
// Certificate.VerifyHostname or the platform verifier.
|
||||
DNSName string
|
||||
|
||||
// Intermediates is an optional pool of certificates that are not trust
|
||||
// anchors, but can be used to form a chain from the leaf certificate to a
|
||||
// root certificate.
|
||||
Intermediates *CertPool
|
||||
Roots *CertPool // if nil, the system roots are used
|
||||
CurrentTime time.Time // if zero, the current time is used
|
||||
// KeyUsage specifies which Extended Key Usage values are acceptable. A leaf
|
||||
// certificate is accepted if it contains any of the listed values. An empty
|
||||
// list means ExtKeyUsageServerAuth. To accept any key usage, include
|
||||
// ExtKeyUsageAny.
|
||||
//
|
||||
// Certificate chains are required to nest these extended key usage values.
|
||||
// (This matches the Windows CryptoAPI behavior, but not the spec.)
|
||||
// Roots is the set of trusted root certificates the leaf certificate needs
|
||||
// to chain up to. If nil, the system roots or the platform verifier are used.
|
||||
Roots *CertPool
|
||||
|
||||
// CurrentTime is used to check the validity of all certificates in the
|
||||
// chain. If zero, the current time is used.
|
||||
CurrentTime time.Time
|
||||
|
||||
// KeyUsages specifies which Extended Key Usage values are acceptable. A
|
||||
// chain is accepted if it allows any of the listed values. An empty list
|
||||
// means ExtKeyUsageServerAuth. To accept any key usage, include ExtKeyUsageAny.
|
||||
KeyUsages []ExtKeyUsage
|
||||
|
||||
// MaxConstraintComparisions is the maximum number of comparisons to
|
||||
// perform when checking a given certificate's name constraints. If
|
||||
// zero, a sensible default is used. This limit prevents pathological
|
||||
// certificates from consuming excessive amounts of CPU time when
|
||||
// validating.
|
||||
// validating. It does not apply to the platform verifier.
|
||||
MaxConstraintComparisions int
|
||||
}
|
||||
|
||||
@@ -717,8 +726,9 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
|
||||
// needed. If successful, it returns one or more chains where the first
|
||||
// element of the chain is c and the last element is from opts.Roots.
|
||||
//
|
||||
// If opts.Roots is nil and system roots are unavailable the returned error
|
||||
// will be of type SystemRootsError.
|
||||
// If opts.Roots is nil, the platform verifier might be used, and
|
||||
// verification details might differ from what is described below. If system
|
||||
// roots are unavailable the returned error will be of type SystemRootsError.
|
||||
//
|
||||
// Name constraints in the intermediates will be applied to all names claimed
|
||||
// in the chain, not just opts.DNSName. Thus it is invalid for a leaf to claim
|
||||
@@ -726,9 +736,10 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
|
||||
// the name being validated. Note that DirectoryName constraints are not
|
||||
// supported.
|
||||
//
|
||||
// Extended Key Usage values are enforced down a chain, so an intermediate or
|
||||
// root that enumerates EKUs prevents a leaf from asserting an EKU not in that
|
||||
// list.
|
||||
// Extended Key Usage values are enforced nested down a chain, so an intermediate
|
||||
// or root that enumerates EKUs prevents a leaf from asserting an EKU not in that
|
||||
// list. (While this is not specified, it is common practice in order to limit
|
||||
// the types of certificates a CA can issue.)
|
||||
//
|
||||
// WARNING: this function doesn't do any revocation checking.
|
||||
func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1217,6 +1217,11 @@ func (d *decodeState) unquoteBytes(s []byte) (t []byte, ok bool) {
|
||||
if r == -1 {
|
||||
return s, true
|
||||
}
|
||||
// Only perform up to one safe unquote for each re-scanned string
|
||||
// literal. In some edge cases, the decoder unquotes a literal a second
|
||||
// time, even after another literal has been re-scanned. Thus, only the
|
||||
// first unquote can safely use safeUnquote.
|
||||
d.safeUnquote = 0
|
||||
|
||||
b := make([]byte, len(s)+2*utf8.UTFMax)
|
||||
w := copy(b, s[0:r])
|
||||
|
||||
@@ -2419,7 +2419,7 @@ func (m *textUnmarshalerString) UnmarshalText(text []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Test unmarshal to a map, with map key is a user defined type.
|
||||
// Test unmarshal to a map, where the map key is a user defined type.
|
||||
// See golang.org/issues/34437.
|
||||
func TestUnmarshalMapWithTextUnmarshalerStringKey(t *testing.T) {
|
||||
var p map[textUnmarshalerString]string
|
||||
@@ -2428,6 +2428,35 @@ func TestUnmarshalMapWithTextUnmarshalerStringKey(t *testing.T) {
|
||||
}
|
||||
|
||||
if _, ok := p["foo"]; !ok {
|
||||
t.Errorf(`Key "foo" is not existed in map: %v`, p)
|
||||
t.Errorf(`Key "foo" does not exist in map: %v`, p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalRescanLiteralMangledUnquote(t *testing.T) {
|
||||
// See golang.org/issues/38105.
|
||||
var p map[textUnmarshalerString]string
|
||||
if err := Unmarshal([]byte(`{"开源":"12345开源"}`), &p); err != nil {
|
||||
t.Fatalf("Unmarshal unexpected error: %v", err)
|
||||
}
|
||||
if _, ok := p["开源"]; !ok {
|
||||
t.Errorf(`Key "开源" does not exist in map: %v`, p)
|
||||
}
|
||||
|
||||
// See golang.org/issues/38126.
|
||||
type T struct {
|
||||
F1 string `json:"F1,string"`
|
||||
}
|
||||
t1 := T{"aaa\tbbb"}
|
||||
|
||||
b, err := Marshal(t1)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal unexpected error: %v", err)
|
||||
}
|
||||
var t2 T
|
||||
if err := Unmarshal(b, &t2); err != nil {
|
||||
t.Fatalf("Unmarshal unexpected error: %v", err)
|
||||
}
|
||||
if t1 != t2 {
|
||||
t.Errorf("Marshal and Unmarshal roundtrip mismatch: want %q got %q", t1, t2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -635,11 +635,12 @@ func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) {
|
||||
return
|
||||
}
|
||||
if opts.quoted {
|
||||
b := make([]byte, 0, v.Len()+2)
|
||||
b = append(b, '"')
|
||||
b = append(b, []byte(v.String())...)
|
||||
b = append(b, '"')
|
||||
e.stringBytes(b, opts.escapeHTML)
|
||||
e2 := newEncodeState()
|
||||
// Since we encode the string twice, we only need to escape HTML
|
||||
// the first time.
|
||||
e2.string(v.String(), opts.escapeHTML)
|
||||
e.stringBytes(e2.Bytes(), false)
|
||||
encodeStatePool.Put(e2)
|
||||
} else {
|
||||
e.string(v.String(), opts.escapeHTML)
|
||||
}
|
||||
|
||||
@@ -79,37 +79,66 @@ type StringTag struct {
|
||||
NumberStr Number `json:",string"`
|
||||
}
|
||||
|
||||
var stringTagExpected = `{
|
||||
"BoolStr": "true",
|
||||
"IntStr": "42",
|
||||
"UintptrStr": "44",
|
||||
"StrStr": "\"xzbit\"",
|
||||
"NumberStr": "46"
|
||||
}`
|
||||
func TestRoundtripStringTag(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in StringTag
|
||||
want string // empty to just test that we roundtrip
|
||||
}{
|
||||
{
|
||||
name: "AllTypes",
|
||||
in: StringTag{
|
||||
BoolStr: true,
|
||||
IntStr: 42,
|
||||
UintptrStr: 44,
|
||||
StrStr: "xzbit",
|
||||
NumberStr: "46",
|
||||
},
|
||||
want: `{
|
||||
"BoolStr": "true",
|
||||
"IntStr": "42",
|
||||
"UintptrStr": "44",
|
||||
"StrStr": "\"xzbit\"",
|
||||
"NumberStr": "46"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
// See golang.org/issues/38173.
|
||||
name: "StringDoubleEscapes",
|
||||
in: StringTag{
|
||||
StrStr: "\b\f\n\r\t\"\\",
|
||||
NumberStr: "0", // just to satisfy the roundtrip
|
||||
},
|
||||
want: `{
|
||||
"BoolStr": "false",
|
||||
"IntStr": "0",
|
||||
"UintptrStr": "0",
|
||||
"StrStr": "\"\\u0008\\u000c\\n\\r\\t\\\"\\\\\"",
|
||||
"NumberStr": "0"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// Indent with a tab prefix to make the multi-line string
|
||||
// literals in the table nicer to read.
|
||||
got, err := MarshalIndent(&test.in, "\t\t\t", "\t")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := string(got); got != test.want {
|
||||
t.Fatalf(" got: %s\nwant: %s\n", got, test.want)
|
||||
}
|
||||
|
||||
func TestStringTag(t *testing.T) {
|
||||
var s StringTag
|
||||
s.BoolStr = true
|
||||
s.IntStr = 42
|
||||
s.UintptrStr = 44
|
||||
s.StrStr = "xzbit"
|
||||
s.NumberStr = "46"
|
||||
got, err := MarshalIndent(&s, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := string(got); got != stringTagExpected {
|
||||
t.Fatalf(" got: %s\nwant: %s\n", got, stringTagExpected)
|
||||
}
|
||||
|
||||
// Verify that it round-trips.
|
||||
var s2 StringTag
|
||||
err = NewDecoder(bytes.NewReader(got)).Decode(&s2)
|
||||
if err != nil {
|
||||
t.Fatalf("Decode: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(s, s2) {
|
||||
t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", s, string(got), s2)
|
||||
// Verify that it round-trips.
|
||||
var s2 StringTag
|
||||
if err := Unmarshal(got, &s2); err != nil {
|
||||
t.Fatalf("Decode: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(test.in, s2) {
|
||||
t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", test.in, string(got), s2)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,14 +144,15 @@ func TestEncoderSetEscapeHTML(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"stringOption", stringOption,
|
||||
`{"bar":"\"\u003chtml\u003efoobar\u003c/html\u003e\""}`,
|
||||
`{"bar":"\"\\u003chtml\\u003efoobar\\u003c/html\\u003e\""}`,
|
||||
`{"bar":"\"<html>foobar</html>\""}`,
|
||||
},
|
||||
} {
|
||||
var buf bytes.Buffer
|
||||
enc := NewEncoder(&buf)
|
||||
if err := enc.Encode(tt.v); err != nil {
|
||||
t.Fatalf("Encode(%s): %s", tt.name, err)
|
||||
t.Errorf("Encode(%s): %s", tt.name, err)
|
||||
continue
|
||||
}
|
||||
if got := strings.TrimSpace(buf.String()); got != tt.wantEscape {
|
||||
t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.wantEscape)
|
||||
@@ -159,7 +160,8 @@ func TestEncoderSetEscapeHTML(t *testing.T) {
|
||||
buf.Reset()
|
||||
enc.SetEscapeHTML(false)
|
||||
if err := enc.Encode(tt.v); err != nil {
|
||||
t.Fatalf("SetEscapeHTML(false) Encode(%s): %s", tt.name, err)
|
||||
t.Errorf("SetEscapeHTML(false) Encode(%s): %s", tt.name, err)
|
||||
continue
|
||||
}
|
||||
if got := strings.TrimSpace(buf.String()); got != tt.want {
|
||||
t.Errorf("SetEscapeHTML(false) Encode(%s) = %#q, want %#q",
|
||||
|
||||
@@ -153,6 +153,7 @@ var pkgDeps = map[string][]string{
|
||||
"internal/syscall/unix": {"L0", "syscall"},
|
||||
"internal/syscall/windows": {"L0", "syscall", "internal/syscall/windows/sysdll", "unicode/utf16"},
|
||||
"internal/syscall/windows/registry": {"L0", "syscall", "internal/syscall/windows/sysdll", "unicode/utf16"},
|
||||
"internal/syscall/execenv": {"L0", "syscall", "internal/syscall/windows", "unicode/utf16"},
|
||||
"time": {
|
||||
// "L0" without the "io" package:
|
||||
"errors",
|
||||
@@ -170,10 +171,10 @@ var pkgDeps = map[string][]string{
|
||||
"internal/cfg": {"L0"},
|
||||
"internal/poll": {"L0", "internal/oserror", "internal/race", "syscall", "time", "unicode/utf16", "unicode/utf8", "internal/syscall/windows", "internal/syscall/unix"},
|
||||
"internal/testlog": {"L0"},
|
||||
"os": {"L1", "os", "syscall", "time", "internal/oserror", "internal/poll", "internal/syscall/windows", "internal/syscall/unix", "internal/testlog"},
|
||||
"os": {"L1", "os", "syscall", "time", "internal/oserror", "internal/poll", "internal/syscall/windows", "internal/syscall/unix", "internal/syscall/execenv", "internal/testlog"},
|
||||
"path/filepath": {"L2", "os", "syscall", "internal/syscall/windows"},
|
||||
"io/ioutil": {"L2", "os", "path/filepath", "time"},
|
||||
"os/exec": {"L2", "os", "context", "path/filepath", "syscall"},
|
||||
"os/exec": {"L2", "os", "context", "path/filepath", "syscall", "internal/syscall/execenv"},
|
||||
"os/signal": {"L2", "os", "syscall"},
|
||||
|
||||
// OS enables basic operating system functionality,
|
||||
|
||||
@@ -62,9 +62,6 @@ func Examples(testFiles ...*ast.File) []*Example {
|
||||
if !ok || f.Recv != nil {
|
||||
continue
|
||||
}
|
||||
if params := f.Type.Params; len(params.List) != 0 {
|
||||
continue // function has params; not a valid example
|
||||
}
|
||||
numDecl++
|
||||
name := f.Name.Name
|
||||
if isTest(name, "Test") || isTest(name, "Benchmark") {
|
||||
@@ -74,6 +71,9 @@ func Examples(testFiles ...*ast.File) []*Example {
|
||||
if !isTest(name, "Example") {
|
||||
continue
|
||||
}
|
||||
if params := f.Type.Params; len(params.List) != 0 {
|
||||
continue // function has params; not a valid example
|
||||
}
|
||||
if f.Body == nil { // ast.File.Body nil dereference (see issue 28044)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -331,25 +331,65 @@ func main() {
|
||||
}
|
||||
`
|
||||
|
||||
const exampleWholeFileFunction = `package foo_test
|
||||
|
||||
func Foo(x int) {
|
||||
}
|
||||
|
||||
func Example() {
|
||||
fmt.Println("Hello, world!")
|
||||
// Output: Hello, world!
|
||||
}
|
||||
`
|
||||
|
||||
const exampleWholeFileFunctionOutput = `package main
|
||||
|
||||
func Foo(x int) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, world!")
|
||||
}
|
||||
`
|
||||
|
||||
var exampleWholeFileTestCases = []struct {
|
||||
Title, Source, Play, Output string
|
||||
}{
|
||||
{
|
||||
"Methods",
|
||||
exampleWholeFile,
|
||||
exampleWholeFileOutput,
|
||||
"Hello, world!\n",
|
||||
},
|
||||
{
|
||||
"Function",
|
||||
exampleWholeFileFunction,
|
||||
exampleWholeFileFunctionOutput,
|
||||
"Hello, world!\n",
|
||||
},
|
||||
}
|
||||
|
||||
func TestExamplesWholeFile(t *testing.T) {
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleWholeFile), parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
es := doc.Examples(file)
|
||||
if len(es) != 1 {
|
||||
t.Fatalf("wrong number of examples; got %d want 1", len(es))
|
||||
}
|
||||
e := es[0]
|
||||
if e.Name != "" {
|
||||
t.Errorf("got Name == %q, want %q", e.Name, "")
|
||||
}
|
||||
if g, w := formatFile(t, fset, e.Play), exampleWholeFileOutput; g != w {
|
||||
t.Errorf("got Play == %q, want %q", g, w)
|
||||
}
|
||||
if g, w := e.Output, "Hello, world!\n"; g != w {
|
||||
t.Errorf("got Output == %q, want %q", g, w)
|
||||
for _, c := range exampleWholeFileTestCases {
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", strings.NewReader(c.Source), parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
es := doc.Examples(file)
|
||||
if len(es) != 1 {
|
||||
t.Fatalf("%s: wrong number of examples; got %d want 1", c.Title, len(es))
|
||||
}
|
||||
e := es[0]
|
||||
if e.Name != "" {
|
||||
t.Errorf("%s: got Name == %q, want %q", c.Title, e.Name, "")
|
||||
}
|
||||
if g, w := formatFile(t, fset, e.Play), c.Play; g != w {
|
||||
t.Errorf("%s: got Play == %q, want %q", c.Title, g, w)
|
||||
}
|
||||
if g, w := e.Output, c.Output; g != w {
|
||||
t.Errorf("%s: got Output == %q, want %q", c.Title, g, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -133,13 +133,7 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode)
|
||||
// first error encountered are returned.
|
||||
//
|
||||
func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, mode Mode) (pkgs map[string]*ast.Package, first error) {
|
||||
fd, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
list, err := fd.Readdir(-1)
|
||||
list, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ type Hash struct {
|
||||
// which does call h.initSeed.)
|
||||
func (h *Hash) initSeed() {
|
||||
if h.seed.s == 0 {
|
||||
h.SetSeed(MakeSeed())
|
||||
h.setSeed(MakeSeed())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,12 +124,17 @@ func (h *Hash) Seed() Seed {
|
||||
// Two Hash objects with different seeds will very likely behave differently.
|
||||
// Any bytes added to h before this call will be discarded.
|
||||
func (h *Hash) SetSeed(seed Seed) {
|
||||
h.setSeed(seed)
|
||||
h.n = 0
|
||||
}
|
||||
|
||||
// setSeed sets seed without discarding accumulated data.
|
||||
func (h *Hash) setSeed(seed Seed) {
|
||||
if seed.s == 0 {
|
||||
panic("maphash: use of uninitialized Seed")
|
||||
}
|
||||
h.seed = seed
|
||||
h.state = seed
|
||||
h.n = 0
|
||||
}
|
||||
|
||||
// Reset discards all bytes added to h.
|
||||
|
||||
@@ -83,6 +83,29 @@ func TestHashHighBytes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepeat(t *testing.T) {
|
||||
h1 := new(Hash)
|
||||
h1.WriteString("testing")
|
||||
sum1 := h1.Sum64()
|
||||
|
||||
h1.Reset()
|
||||
h1.WriteString("testing")
|
||||
sum2 := h1.Sum64()
|
||||
|
||||
if sum1 != sum2 {
|
||||
t.Errorf("different sum after reseting: %#x != %#x", sum1, sum2)
|
||||
}
|
||||
|
||||
h2 := new(Hash)
|
||||
h2.SetSeed(h1.Seed())
|
||||
h2.WriteString("testing")
|
||||
sum3 := h2.Sum64()
|
||||
|
||||
if sum1 != sum3 {
|
||||
t.Errorf("different sum on the same seed: %#x != %#x", sum1, sum3)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure a Hash implements the hash.Hash and hash.Hash64 interfaces.
|
||||
var _ hash.Hash = &Hash{}
|
||||
var _ hash.Hash64 = &Hash{}
|
||||
|
||||
19
src/internal/syscall/execenv/execenv_default.go
Normal file
19
src/internal/syscall/execenv/execenv_default.go
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package execenv
|
||||
|
||||
import "syscall"
|
||||
|
||||
// Default will return the default environment
|
||||
// variables based on the process attributes
|
||||
// provided.
|
||||
//
|
||||
// Defaults to syscall.Environ() on all platforms
|
||||
// other than Windows.
|
||||
func Default(sys *syscall.SysProcAttr) ([]string, error) {
|
||||
return syscall.Environ(), nil
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package os
|
||||
// +build windows
|
||||
|
||||
package execenv
|
||||
|
||||
import (
|
||||
"internal/syscall/windows"
|
||||
@@ -11,9 +13,17 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func environForSysProcAttr(sys *syscall.SysProcAttr) (env []string, err error) {
|
||||
// Default will return the default environment
|
||||
// variables based on the process attributes
|
||||
// provided.
|
||||
//
|
||||
// If the process attributes contain a token, then
|
||||
// the environment variables will be sourced from
|
||||
// the defaults for that user token, otherwise they
|
||||
// will be sourced from syscall.Environ().
|
||||
func Default(sys *syscall.SysProcAttr) (env []string, err error) {
|
||||
if sys == nil || sys.Token == 0 {
|
||||
return Environ(), nil
|
||||
return syscall.Environ(), nil
|
||||
}
|
||||
var block *uint16
|
||||
err = windows.CreateEnvironmentBlock(&block, sys.Token, false)
|
||||
@@ -740,7 +740,8 @@ func (z nat) divLarge(u, uIn, vIn nat) (q, r nat) {
|
||||
// The remainder overwrites input u.
|
||||
//
|
||||
// Precondition:
|
||||
// - len(q) >= len(u)-len(v)
|
||||
// - q is large enough to hold the quotient u / v
|
||||
// which has a maximum length of len(u)-len(v)+1.
|
||||
func (q nat) divBasic(u, v nat) {
|
||||
n := len(v)
|
||||
m := len(u) - n
|
||||
@@ -779,6 +780,8 @@ func (q nat) divBasic(u, v nat) {
|
||||
}
|
||||
|
||||
// D4.
|
||||
// Compute the remainder u - (q̂*v) << (_W*j).
|
||||
// The subtraction may overflow if q̂ estimate was off by one.
|
||||
qhatv[n] = mulAddVWW(qhatv[0:n], v, qhat, 0)
|
||||
qhl := len(qhatv)
|
||||
if j+qhl > len(u) && qhatv[n] == 0 {
|
||||
@@ -787,7 +790,11 @@ func (q nat) divBasic(u, v nat) {
|
||||
c := subVV(u[j:j+qhl], u[j:], qhatv)
|
||||
if c != 0 {
|
||||
c := addVV(u[j:j+n], u[j:], v)
|
||||
u[j+n] += c
|
||||
// If n == qhl, the carry from subVV and the carry from addVV
|
||||
// cancel out and don't affect u[j+n].
|
||||
if n < qhl {
|
||||
u[j+n] += c
|
||||
}
|
||||
qhat--
|
||||
}
|
||||
|
||||
@@ -827,6 +834,10 @@ func (z nat) divRecursive(u, v nat) {
|
||||
putNat(tmp)
|
||||
}
|
||||
|
||||
// divRecursiveStep computes the division of u by v.
|
||||
// - z must be large enough to hold the quotient
|
||||
// - the quotient will overwrite z
|
||||
// - the remainder will overwrite u
|
||||
func (z nat) divRecursiveStep(u, v nat, depth int, tmp *nat, temps []*nat) {
|
||||
u = u.norm()
|
||||
v = v.norm()
|
||||
|
||||
@@ -786,3 +786,21 @@ func TestNatDiv(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestIssue37499 triggers the edge case of divBasic where
|
||||
// the inaccurate estimate of the first word's quotient
|
||||
// happens at the very beginning of the loop.
|
||||
func TestIssue37499(t *testing.T) {
|
||||
// Choose u and v such that v is slightly larger than u >> N.
|
||||
// This tricks divBasic into choosing 1 as the first word
|
||||
// of the quotient. This works in both 32-bit and 64-bit settings.
|
||||
u := natFromString("0x2b6c385a05be027f5c22005b63c42a1165b79ff510e1706b39f8489c1d28e57bb5ba4ef9fd9387a3e344402c0a453381")
|
||||
v := natFromString("0x2b6c385a05be027f5c22005b63c42a1165b79ff510e1706c")
|
||||
|
||||
q := nat(nil).make(8)
|
||||
q.divBasic(u, v)
|
||||
q = q.norm()
|
||||
if s := string(q.utoa(16)); s != "fffffffffffffffffffffffffffffffffffffffffffffffb" {
|
||||
t.Fatalf("incorrect quotient: %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,6 +425,16 @@ type response struct {
|
||||
wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive"
|
||||
wantsClose bool // HTTP request has Connection "close"
|
||||
|
||||
// canWriteContinue is a boolean value accessed as an atomic int32
|
||||
// that says whether or not a 100 Continue header can be written
|
||||
// to the connection.
|
||||
// writeContinueMu must be held while writing the header.
|
||||
// These two fields together synchronize the body reader
|
||||
// (the expectContinueReader, which wants to write 100 Continue)
|
||||
// against the main writer.
|
||||
canWriteContinue atomicBool
|
||||
writeContinueMu sync.Mutex
|
||||
|
||||
w *bufio.Writer // buffers output in chunks to chunkWriter
|
||||
cw chunkWriter
|
||||
|
||||
@@ -515,6 +525,7 @@ type atomicBool int32
|
||||
|
||||
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
||||
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
|
||||
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
|
||||
|
||||
// declareTrailer is called for each Trailer header when the
|
||||
// response header is written. It notes that a header will need to be
|
||||
@@ -877,21 +888,27 @@ type expectContinueReader struct {
|
||||
resp *response
|
||||
readCloser io.ReadCloser
|
||||
closed bool
|
||||
sawEOF bool
|
||||
sawEOF atomicBool
|
||||
}
|
||||
|
||||
func (ecr *expectContinueReader) Read(p []byte) (n int, err error) {
|
||||
if ecr.closed {
|
||||
return 0, ErrBodyReadAfterClose
|
||||
}
|
||||
if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked() {
|
||||
ecr.resp.wroteContinue = true
|
||||
ecr.resp.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n")
|
||||
ecr.resp.conn.bufw.Flush()
|
||||
w := ecr.resp
|
||||
if !w.wroteContinue && w.canWriteContinue.isSet() && !w.conn.hijacked() {
|
||||
w.wroteContinue = true
|
||||
w.writeContinueMu.Lock()
|
||||
if w.canWriteContinue.isSet() {
|
||||
w.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n")
|
||||
w.conn.bufw.Flush()
|
||||
w.canWriteContinue.setFalse()
|
||||
}
|
||||
w.writeContinueMu.Unlock()
|
||||
}
|
||||
n, err = ecr.readCloser.Read(p)
|
||||
if err == io.EOF {
|
||||
ecr.sawEOF = true
|
||||
ecr.sawEOF.setTrue()
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1310,7 +1327,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
|
||||
// because we don't know if the next bytes on the wire will be
|
||||
// the body-following-the-timer or the subsequent request.
|
||||
// See Issue 11549.
|
||||
if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF {
|
||||
if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF.isSet() {
|
||||
w.closeAfterReply = true
|
||||
}
|
||||
|
||||
@@ -1560,6 +1577,17 @@ func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err er
|
||||
}
|
||||
return 0, ErrHijacked
|
||||
}
|
||||
|
||||
if w.canWriteContinue.isSet() {
|
||||
// Body reader wants to write 100 Continue but hasn't yet.
|
||||
// Tell it not to. The store must be done while holding the lock
|
||||
// because the lock makes sure that there is not an active write
|
||||
// this very moment.
|
||||
w.writeContinueMu.Lock()
|
||||
w.canWriteContinue.setFalse()
|
||||
w.writeContinueMu.Unlock()
|
||||
}
|
||||
|
||||
if !w.wroteHeader {
|
||||
w.WriteHeader(StatusOK)
|
||||
}
|
||||
@@ -1871,6 +1899,7 @@ func (c *conn) serve(ctx context.Context) {
|
||||
if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
|
||||
// Wrap the Body reader with one that replies on the connection
|
||||
req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
|
||||
w.canWriteContinue.setTrue()
|
||||
}
|
||||
} else if req.Header.get("Expect") != "" {
|
||||
w.sendExpectationFailed()
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package os
|
||||
|
||||
import "syscall"
|
||||
|
||||
func environForSysProcAttr(sys *syscall.SysProcAttr) ([]string, error) {
|
||||
return Environ(), nil
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"internal/syscall/execenv"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -222,11 +223,11 @@ func interfaceEqual(a, b interface{}) bool {
|
||||
return a == b
|
||||
}
|
||||
|
||||
func (c *Cmd) envv() []string {
|
||||
func (c *Cmd) envv() ([]string, error) {
|
||||
if c.Env != nil {
|
||||
return c.Env
|
||||
return c.Env, nil
|
||||
}
|
||||
return os.Environ()
|
||||
return execenv.Default(c.SysProcAttr)
|
||||
}
|
||||
|
||||
func (c *Cmd) argv() []string {
|
||||
@@ -413,11 +414,15 @@ func (c *Cmd) Start() error {
|
||||
}
|
||||
c.childFiles = append(c.childFiles, c.ExtraFiles...)
|
||||
|
||||
var err error
|
||||
envv, err := c.envv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{
|
||||
Dir: c.Dir,
|
||||
Files: c.childFiles,
|
||||
Env: addCriticalEnv(dedupEnv(c.envv())),
|
||||
Env: addCriticalEnv(dedupEnv(envv)),
|
||||
Sys: c.SysProcAttr,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
package os
|
||||
|
||||
import (
|
||||
"internal/syscall/execenv"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
@@ -39,7 +40,7 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
|
||||
Sys: attr.Sys,
|
||||
}
|
||||
if sysattr.Env == nil {
|
||||
sysattr.Env, err = environForSysProcAttr(sysattr.Sys)
|
||||
sysattr.Env, err = execenv.Default(sysattr.Sys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2448,3 +2448,38 @@ func TestDirSeek(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that opening a file does not change its permissions. Issue 38225.
|
||||
func TestOpenFileKeepsPermissions(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir, err := ioutil.TempDir("", "TestOpenFileKeepsPermissions")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer RemoveAll(dir)
|
||||
name := filepath.Join(dir, "x")
|
||||
f, err := Create(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
f, err = OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if fi, err := f.Stat(); err != nil {
|
||||
t.Error(err)
|
||||
} else if fi.Mode()&0222 == 0 {
|
||||
t.Errorf("f.Stat.Mode after OpenFile is %v, should be writable", fi.Mode())
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if fi, err := Stat(name); err != nil {
|
||||
t.Error(err)
|
||||
} else if fi.Mode()&0222 == 0 {
|
||||
t.Errorf("Stat after OpenFile is %v, should be writable", fi.Mode())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,9 +158,19 @@ func nilinterhash(p unsafe.Pointer, h uintptr) uintptr {
|
||||
// is slower but more general and is used for hashing interface types
|
||||
// (called from interhash or nilinterhash, above) or for hashing in
|
||||
// maps generated by reflect.MapOf (reflect_typehash, below).
|
||||
// Note: this function must match the compiler generated
|
||||
// functions exactly. See issue 37716.
|
||||
func typehash(t *_type, p unsafe.Pointer, h uintptr) uintptr {
|
||||
if t.tflag&tflagRegularMemory != 0 {
|
||||
return memhash(p, h, t.size)
|
||||
// Handle ptr sizes specially, see issue 37086.
|
||||
switch t.size {
|
||||
case 4:
|
||||
return memhash32(p, h)
|
||||
case 8:
|
||||
return memhash64(p, h)
|
||||
default:
|
||||
return memhash(p, h, t.size)
|
||||
}
|
||||
}
|
||||
switch t.kind & kindMask {
|
||||
case kindFloat32:
|
||||
@@ -187,12 +197,28 @@ func typehash(t *_type, p unsafe.Pointer, h uintptr) uintptr {
|
||||
return h
|
||||
case kindStruct:
|
||||
s := (*structtype)(unsafe.Pointer(t))
|
||||
memStart := uintptr(0)
|
||||
memEnd := uintptr(0)
|
||||
for _, f := range s.fields {
|
||||
// TODO: maybe we could hash several contiguous fields all at once.
|
||||
if memEnd > memStart && (f.name.isBlank() || f.offset() != memEnd || f.typ.tflag&tflagRegularMemory == 0) {
|
||||
// flush any pending regular memory hashing
|
||||
h = memhash(add(p, memStart), h, memEnd-memStart)
|
||||
memStart = memEnd
|
||||
}
|
||||
if f.name.isBlank() {
|
||||
continue
|
||||
}
|
||||
h = typehash(f.typ, add(p, f.offset()), h)
|
||||
if f.typ.tflag&tflagRegularMemory == 0 {
|
||||
h = typehash(f.typ, add(p, f.offset()), h)
|
||||
continue
|
||||
}
|
||||
if memStart == memEnd {
|
||||
memStart = f.offset()
|
||||
}
|
||||
memEnd = f.offset() + f.typ.size
|
||||
}
|
||||
if memEnd > memStart {
|
||||
h = memhash(add(p, memStart), h, memEnd-memStart)
|
||||
}
|
||||
return h
|
||||
default:
|
||||
|
||||
@@ -8,8 +8,10 @@ import "unsafe"
|
||||
|
||||
func checkptrAlignment(p unsafe.Pointer, elem *_type, n uintptr) {
|
||||
// Check that (*[n]elem)(p) is appropriately aligned.
|
||||
// Note that we allow unaligned pointers if the types they point to contain
|
||||
// no pointers themselves. See issue 37298.
|
||||
// TODO(mdempsky): What about fieldAlign?
|
||||
if uintptr(p)&(uintptr(elem.align)-1) != 0 {
|
||||
if elem.ptrdata != 0 && uintptr(p)&(uintptr(elem.align)-1) != 0 {
|
||||
throw("checkptr: unsafe pointer conversion")
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ func TestCheckPtr(t *testing.T) {
|
||||
cmd string
|
||||
want string
|
||||
}{
|
||||
{"CheckPtrAlignment", "fatal error: checkptr: unsafe pointer conversion\n"},
|
||||
{"CheckPtrAlignmentPtr", "fatal error: checkptr: unsafe pointer conversion\n"},
|
||||
{"CheckPtrAlignmentNoPtr", ""},
|
||||
{"CheckPtrArithmetic", "fatal error: checkptr: unsafe pointer arithmetic\n"},
|
||||
{"CheckPtrSize", "fatal error: checkptr: unsafe pointer conversion\n"},
|
||||
{"CheckPtrSmall", "fatal error: checkptr: unsafe pointer arithmetic\n"},
|
||||
@@ -38,6 +39,12 @@ func TestCheckPtr(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
if tc.want == "" {
|
||||
if len(got) > 0 {
|
||||
t.Errorf("output:\n%s\nwant no output", got)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !strings.HasPrefix(string(got), tc.want) {
|
||||
t.Errorf("output:\n%s\n\nwant output starting with: %s", got, tc.want)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
// Offsets into internal/cpu records for use in assembly.
|
||||
const (
|
||||
offsetX86HasAVX = unsafe.Offsetof(cpu.X86.HasAVX)
|
||||
offsetX86HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2)
|
||||
offsetX86HasERMS = unsafe.Offsetof(cpu.X86.HasERMS)
|
||||
offsetX86HasSSE2 = unsafe.Offsetof(cpu.X86.HasSSE2)
|
||||
|
||||
@@ -55,6 +55,16 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return runBuiltTestProg(t, exe, name, env...)
|
||||
}
|
||||
|
||||
func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
|
||||
if *flagQuick {
|
||||
t.Skip("-quick")
|
||||
}
|
||||
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
cmd := testenv.CleanCmdEnv(exec.Command(exe, name))
|
||||
cmd.Env = append(cmd.Env, env...)
|
||||
if testing.Short() {
|
||||
@@ -64,7 +74,7 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string {
|
||||
cmd.Stdout = &b
|
||||
cmd.Stderr = &b
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatalf("starting %s %s: %v", binary, name, err)
|
||||
t.Fatalf("starting %s %s: %v", exe, name, err)
|
||||
}
|
||||
|
||||
// If the process doesn't complete within 1 minute,
|
||||
@@ -92,7 +102,7 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string {
|
||||
}()
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
t.Logf("%s %s exit status: %v", binary, name, err)
|
||||
t.Logf("%s %s exit status: %v", exe, name, err)
|
||||
}
|
||||
close(done)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ package runtime_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
@@ -281,3 +282,122 @@ func TestDeferForFuncWithNoExit(t *testing.T) {
|
||||
for {
|
||||
}
|
||||
}
|
||||
|
||||
// Test case approximating issue #37664, where a recursive function (interpreter)
|
||||
// may do repeated recovers/re-panics until it reaches the frame where the panic
|
||||
// can actually be handled. The recurseFnPanicRec() function is testing that there
|
||||
// are no stale defer structs on the defer chain after the interpreter() sequence,
|
||||
// by writing a bunch of 0xffffffffs into several recursive stack frames, and then
|
||||
// doing a single panic-recover which would invoke any such stale defer structs.
|
||||
func TestDeferWithRepeatedRepanics(t *testing.T) {
|
||||
interpreter(0, 6, 2)
|
||||
recurseFnPanicRec(0, 10)
|
||||
interpreter(0, 5, 1)
|
||||
recurseFnPanicRec(0, 10)
|
||||
interpreter(0, 6, 3)
|
||||
recurseFnPanicRec(0, 10)
|
||||
}
|
||||
|
||||
func interpreter(level int, maxlevel int, rec int) {
|
||||
defer func() {
|
||||
e := recover()
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
if level != e.(int) {
|
||||
//fmt.Fprintln(os.Stderr, "re-panicing, level", level)
|
||||
panic(e)
|
||||
}
|
||||
//fmt.Fprintln(os.Stderr, "Recovered, level", level)
|
||||
}()
|
||||
if level+1 < maxlevel {
|
||||
interpreter(level+1, maxlevel, rec)
|
||||
} else {
|
||||
//fmt.Fprintln(os.Stderr, "Initiating panic")
|
||||
panic(rec)
|
||||
}
|
||||
}
|
||||
|
||||
func recurseFnPanicRec(level int, maxlevel int) {
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
recurseFn(level, maxlevel)
|
||||
}
|
||||
|
||||
func recurseFn(level int, maxlevel int) {
|
||||
a := [40]uint32{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}
|
||||
if level+1 < maxlevel {
|
||||
// Need this print statement to keep a around. '_ = a[4]' doesn't do it.
|
||||
fmt.Fprintln(os.Stderr, "recurseFn", level, a[4])
|
||||
recurseFn(level+1, maxlevel)
|
||||
} else {
|
||||
panic("recurseFn panic")
|
||||
}
|
||||
}
|
||||
|
||||
// Try to reproduce issue #37688, where a pointer to an open-coded defer struct is
|
||||
// mistakenly held, and that struct keeps a pointer to a stack-allocated defer
|
||||
// struct, and that stack-allocated struct gets overwritten or the stack gets
|
||||
// moved, so a memory error happens on GC.
|
||||
func TestIssue37688(t *testing.T) {
|
||||
for j := 0; j < 10; j++ {
|
||||
g2()
|
||||
g3()
|
||||
}
|
||||
}
|
||||
|
||||
type foo struct {
|
||||
}
|
||||
|
||||
func (f *foo) method1() {
|
||||
fmt.Fprintln(os.Stderr, "method1")
|
||||
}
|
||||
|
||||
func (f *foo) method2() {
|
||||
fmt.Fprintln(os.Stderr, "method2")
|
||||
}
|
||||
|
||||
func g2() {
|
||||
var a foo
|
||||
ap := &a
|
||||
// The loop forces this defer to be heap-allocated and the remaining two
|
||||
// to be stack-allocated.
|
||||
for i := 0; i < 1; i++ {
|
||||
defer ap.method1()
|
||||
}
|
||||
defer ap.method2()
|
||||
defer ap.method1()
|
||||
ff1(ap, 1, 2, 3, 4, 5, 6, 7, 8, 9)
|
||||
// Try to get the stack to be be moved by growing it too large, so
|
||||
// existing stack-allocated defer becomes invalid.
|
||||
rec1(2000)
|
||||
}
|
||||
|
||||
func g3() {
|
||||
// Mix up the stack layout by adding in an extra function frame
|
||||
g2()
|
||||
}
|
||||
|
||||
func ff1(ap *foo, a, b, c, d, e, f, g, h, i int) {
|
||||
defer ap.method1()
|
||||
|
||||
// Make a defer that has a very large set of args, hence big size for the
|
||||
// defer record for the open-coded frame (which means it won't use the
|
||||
// defer pool)
|
||||
defer func(ap *foo, a, b, c, d, e, f, g, h, i int) {
|
||||
if v := recover(); v != nil {
|
||||
fmt.Fprintln(os.Stderr, "did recover")
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "debug", ap, a, b, c, d, e, f, g, h)
|
||||
}(ap, a, b, c, d, e, f, g, h, i)
|
||||
panic("ff1 panic")
|
||||
}
|
||||
|
||||
func rec1(max int) {
|
||||
if max > 0 {
|
||||
rec1(max - 1)
|
||||
} else {
|
||||
fmt.Fprintln(os.Stderr, "finished recursion", max)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -950,3 +950,28 @@ func SemNwait(addr *uint32) uint32 {
|
||||
root := semroot(addr)
|
||||
return atomic.Load(&root.nwait)
|
||||
}
|
||||
|
||||
// MapHashCheck computes the hash of the key k for the map m, twice.
|
||||
// Method 1 uses the built-in hasher for the map.
|
||||
// Method 2 uses the typehash function (the one used by reflect).
|
||||
// Returns the two hash values, which should always be equal.
|
||||
func MapHashCheck(m interface{}, k interface{}) (uintptr, uintptr) {
|
||||
// Unpack m.
|
||||
mt := (*maptype)(unsafe.Pointer(efaceOf(&m)._type))
|
||||
mh := (*hmap)(efaceOf(&m).data)
|
||||
|
||||
// Unpack k.
|
||||
kt := efaceOf(&k)._type
|
||||
var p unsafe.Pointer
|
||||
if isDirectIface(kt) {
|
||||
q := efaceOf(&k).data
|
||||
p = unsafe.Pointer(&q)
|
||||
} else {
|
||||
p = efaceOf(&k).data
|
||||
}
|
||||
|
||||
// Compute the hash functions.
|
||||
x := mt.hasher(noescape(p), uintptr(mh.hash0))
|
||||
y := typehash(kt, noescape(p), uintptr(mh.hash0))
|
||||
return x, y
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
. "runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -48,6 +49,54 @@ func TestMemHash64Equality(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompilerVsRuntimeHash(t *testing.T) {
|
||||
// Test to make sure the compiler's hash function and the runtime's hash function agree.
|
||||
// See issue 37716.
|
||||
for _, m := range []interface{}{
|
||||
map[bool]int{},
|
||||
map[int8]int{},
|
||||
map[uint8]int{},
|
||||
map[int16]int{},
|
||||
map[uint16]int{},
|
||||
map[int32]int{},
|
||||
map[uint32]int{},
|
||||
map[int64]int{},
|
||||
map[uint64]int{},
|
||||
map[int]int{},
|
||||
map[uint]int{},
|
||||
map[uintptr]int{},
|
||||
map[*byte]int{},
|
||||
map[chan int]int{},
|
||||
map[unsafe.Pointer]int{},
|
||||
map[float32]int{},
|
||||
map[float64]int{},
|
||||
map[complex64]int{},
|
||||
map[complex128]int{},
|
||||
map[string]int{},
|
||||
//map[interface{}]int{},
|
||||
//map[interface{F()}]int{},
|
||||
map[[8]uint64]int{},
|
||||
map[[8]string]int{},
|
||||
map[struct{ a, b, c, d int32 }]int{}, // Note: tests AMEM128
|
||||
map[struct{ a, b, _, d int32 }]int{},
|
||||
map[struct {
|
||||
a, b int32
|
||||
c float32
|
||||
d, e [8]byte
|
||||
}]int{},
|
||||
map[struct {
|
||||
a int16
|
||||
b int64
|
||||
}]int{},
|
||||
} {
|
||||
k := reflect.New(reflect.TypeOf(m).Key()).Elem().Interface() // the zero key
|
||||
x, y := MapHashCheck(m, k)
|
||||
if x != y {
|
||||
t.Errorf("hashes did not match (%x vs %x) for map %T", x, y, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Smhasher is a torture test for hash functions.
|
||||
// https://code.google.com/p/smhasher/
|
||||
// This code is a port of some of the Smhasher tests to Go.
|
||||
|
||||
@@ -1921,7 +1921,11 @@ Run:
|
||||
// The bitmask starts at s.startAddr.
|
||||
// The result must be deallocated with dematerializeGCProg.
|
||||
func materializeGCProg(ptrdata uintptr, prog *byte) *mspan {
|
||||
s := mheap_.allocManual((ptrdata/(8*sys.PtrSize)+pageSize-1)/pageSize, &memstats.gc_sys)
|
||||
// Each word of ptrdata needs one bit in the bitmap.
|
||||
bitmapBytes := divRoundUp(ptrdata, 8*sys.PtrSize)
|
||||
// Compute the number of pages needed for bitmapBytes.
|
||||
pages := divRoundUp(bitmapBytes, pageSize)
|
||||
s := mheap_.allocManual(pages, &memstats.gc_sys)
|
||||
runGCProg(addb(prog, 4), nil, (*byte)(unsafe.Pointer(s.startAddr)), 1)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -769,32 +769,40 @@ func gcSetTriggerRatio(triggerRatio float64) {
|
||||
goal = memstats.heap_marked + memstats.heap_marked*uint64(gcpercent)/100
|
||||
}
|
||||
|
||||
// If we let triggerRatio go too low, then if the application
|
||||
// is allocating very rapidly we might end up in a situation
|
||||
// where we're allocating black during a nearly always-on GC.
|
||||
// The result of this is a growing heap and ultimately an
|
||||
// increase in RSS. By capping us at a point >0, we're essentially
|
||||
// saying that we're OK using more CPU during the GC to prevent
|
||||
// this growth in RSS.
|
||||
//
|
||||
// The current constant was chosen empirically: given a sufficiently
|
||||
// fast/scalable allocator with 48 Ps that could drive the trigger ratio
|
||||
// to <0.05, this constant causes applications to retain the same peak
|
||||
// RSS compared to not having this allocator.
|
||||
const minTriggerRatio = 0.6
|
||||
|
||||
// Set the trigger ratio, capped to reasonable bounds.
|
||||
if triggerRatio < minTriggerRatio {
|
||||
// This can happen if the mutator is allocating very
|
||||
// quickly or the GC is scanning very slowly.
|
||||
triggerRatio = minTriggerRatio
|
||||
} else if gcpercent >= 0 {
|
||||
if gcpercent >= 0 {
|
||||
scalingFactor := float64(gcpercent) / 100
|
||||
// Ensure there's always a little margin so that the
|
||||
// mutator assist ratio isn't infinity.
|
||||
maxTriggerRatio := 0.95 * float64(gcpercent) / 100
|
||||
maxTriggerRatio := 0.95 * scalingFactor
|
||||
if triggerRatio > maxTriggerRatio {
|
||||
triggerRatio = maxTriggerRatio
|
||||
}
|
||||
|
||||
// If we let triggerRatio go too low, then if the application
|
||||
// is allocating very rapidly we might end up in a situation
|
||||
// where we're allocating black during a nearly always-on GC.
|
||||
// The result of this is a growing heap and ultimately an
|
||||
// increase in RSS. By capping us at a point >0, we're essentially
|
||||
// saying that we're OK using more CPU during the GC to prevent
|
||||
// this growth in RSS.
|
||||
//
|
||||
// The current constant was chosen empirically: given a sufficiently
|
||||
// fast/scalable allocator with 48 Ps that could drive the trigger ratio
|
||||
// to <0.05, this constant causes applications to retain the same peak
|
||||
// RSS compared to not having this allocator.
|
||||
minTriggerRatio := 0.6 * scalingFactor
|
||||
if triggerRatio < minTriggerRatio {
|
||||
triggerRatio = minTriggerRatio
|
||||
}
|
||||
} else if triggerRatio < 0 {
|
||||
// gcpercent < 0, so just make sure we're not getting a negative
|
||||
// triggerRatio. This case isn't expected to happen in practice,
|
||||
// and doesn't really matter because if gcpercent < 0 then we won't
|
||||
// ever consume triggerRatio further on in this function, but let's
|
||||
// just be defensive here; the triggerRatio being negative is almost
|
||||
// certainly undesirable.
|
||||
triggerRatio = 0
|
||||
}
|
||||
memstats.triggerRatio = triggerRatio
|
||||
|
||||
|
||||
@@ -286,6 +286,28 @@ func bgscavenge(c chan int) {
|
||||
continue
|
||||
}
|
||||
|
||||
if released < physPageSize {
|
||||
// If this happens, it means that we may have attempted to release part
|
||||
// of a physical page, but the likely effect of that is that it released
|
||||
// the whole physical page, some of which may have still been in-use.
|
||||
// This could lead to memory corruption. Throw.
|
||||
throw("released less than one physical page of memory")
|
||||
}
|
||||
|
||||
// On some platforms we may see crit as zero if the time it takes to scavenge
|
||||
// memory is less than the minimum granularity of its clock (e.g. Windows).
|
||||
// In this case, just assume scavenging takes 10 µs per regular physical page
|
||||
// (determined empirically), and conservatively ignore the impact of huge pages
|
||||
// on timing.
|
||||
//
|
||||
// We shouldn't ever see a crit value less than zero unless there's a bug of
|
||||
// some kind, either on our side or in the platform we're running on, but be
|
||||
// defensive in that case as well.
|
||||
const approxCritNSPerPhysicalPage = 10e3
|
||||
if crit <= 0 {
|
||||
crit = approxCritNSPerPhysicalPage * float64(released/physPageSize)
|
||||
}
|
||||
|
||||
// Multiply the critical time by 1 + the ratio of the costs of using
|
||||
// scavenged memory vs. scavenging memory. This forces us to pay down
|
||||
// the cost of reusing this memory eagerly by sleeping for a longer period
|
||||
|
||||
@@ -244,15 +244,6 @@ func genAMD64() {
|
||||
|
||||
// TODO: MXCSR register?
|
||||
|
||||
// Apparently, the signal handling code path in darwin kernel leaves
|
||||
// the upper bits of Y registers in a dirty state, which causes
|
||||
// many SSE operations (128-bit and narrower) become much slower.
|
||||
// Clear the upper bits to get to a clean state. See issue #37174.
|
||||
// It is safe here as Go code don't use the upper bits of Y registers.
|
||||
p("#ifdef GOOS_darwin")
|
||||
p("VZEROUPPER")
|
||||
p("#endif")
|
||||
|
||||
p("PUSHQ BP")
|
||||
p("MOVQ SP, BP")
|
||||
p("// Save flags before clobbering them")
|
||||
@@ -261,6 +252,18 @@ func genAMD64() {
|
||||
p("ADJSP $%d", l.stack)
|
||||
p("// But vet doesn't know ADJSP, so suppress vet stack checking")
|
||||
p("NOP SP")
|
||||
|
||||
// Apparently, the signal handling code path in darwin kernel leaves
|
||||
// the upper bits of Y registers in a dirty state, which causes
|
||||
// many SSE operations (128-bit and narrower) become much slower.
|
||||
// Clear the upper bits to get to a clean state. See issue #37174.
|
||||
// It is safe here as Go code don't use the upper bits of Y registers.
|
||||
p("#ifdef GOOS_darwin")
|
||||
p("CMPB internal∕cpu·X86+const_offsetX86HasAVX(SB), $0")
|
||||
p("JE 2(PC)")
|
||||
p("VZEROUPPER")
|
||||
p("#endif")
|
||||
|
||||
l.save()
|
||||
p("CALL ·asyncPreempt2(SB)")
|
||||
l.restore()
|
||||
@@ -379,6 +382,7 @@ func genMIPS(_64bit bool) {
|
||||
sub := "SUB"
|
||||
r28 := "R28"
|
||||
regsize := 4
|
||||
softfloat := "GOMIPS_softfloat"
|
||||
if _64bit {
|
||||
mov = "MOVV"
|
||||
movf = "MOVD"
|
||||
@@ -386,6 +390,7 @@ func genMIPS(_64bit bool) {
|
||||
sub = "SUBV"
|
||||
r28 = "RSB"
|
||||
regsize = 8
|
||||
softfloat = "GOMIPS64_softfloat"
|
||||
}
|
||||
|
||||
// Add integer registers R1-R22, R24-R25, R28
|
||||
@@ -408,28 +413,36 @@ func genMIPS(_64bit bool) {
|
||||
mov+" LO, R1\n"+mov+" R1, %d(R29)",
|
||||
mov+" %d(R29), R1\n"+mov+" R1, LO",
|
||||
regsize)
|
||||
|
||||
// Add floating point control/status register FCR31 (FCR0-FCR30 are irrelevant)
|
||||
l.addSpecial(
|
||||
var lfp = layout{sp: "R29", stack: l.stack}
|
||||
lfp.addSpecial(
|
||||
mov+" FCR31, R1\n"+mov+" R1, %d(R29)",
|
||||
mov+" %d(R29), R1\n"+mov+" R1, FCR31",
|
||||
regsize)
|
||||
// Add floating point registers F0-F31.
|
||||
for i := 0; i <= 31; i++ {
|
||||
reg := fmt.Sprintf("F%d", i)
|
||||
l.add(movf, reg, regsize)
|
||||
lfp.add(movf, reg, regsize)
|
||||
}
|
||||
|
||||
// allocate frame, save PC of interrupted instruction (in LR)
|
||||
p(mov+" R31, -%d(R29)", l.stack)
|
||||
p(sub+" $%d, R29", l.stack)
|
||||
p(mov+" R31, -%d(R29)", lfp.stack)
|
||||
p(sub+" $%d, R29", lfp.stack)
|
||||
|
||||
l.save()
|
||||
p("#ifndef %s", softfloat)
|
||||
lfp.save()
|
||||
p("#endif")
|
||||
p("CALL ·asyncPreempt2(SB)")
|
||||
p("#ifndef %s", softfloat)
|
||||
lfp.restore()
|
||||
p("#endif")
|
||||
l.restore()
|
||||
|
||||
p(mov+" %d(R29), R31", l.stack) // sigctxt.pushCall has pushed LR (at interrupt) on stack, restore it
|
||||
p(mov + " (R29), R23") // load PC to REGTMP
|
||||
p(add+" $%d, R29", l.stack+regsize) // pop frame (including the space pushed by sigctxt.pushCall)
|
||||
p(mov+" %d(R29), R31", lfp.stack) // sigctxt.pushCall has pushed LR (at interrupt) on stack, restore it
|
||||
p(mov + " (R29), R23") // load PC to REGTMP
|
||||
p(add+" $%d, R29", lfp.stack+regsize) // pop frame (including the space pushed by sigctxt.pushCall)
|
||||
p("JMP (R23)")
|
||||
}
|
||||
|
||||
|
||||
@@ -148,9 +148,14 @@ func (s *pageAlloc) allocToCache() pageCache {
|
||||
// Update as an allocation, but note that it's not contiguous.
|
||||
s.update(c.base, pageCachePages, false, true)
|
||||
|
||||
// We're always searching for the first free page, and we always know the
|
||||
// up to pageCache size bits will be allocated, so we can always move the
|
||||
// searchAddr past the cache.
|
||||
s.searchAddr = c.base + pageSize*pageCachePages
|
||||
// Set the search address to the last page represented by the cache.
|
||||
// Since all of the pages in this block are going to the cache, and we
|
||||
// searched for the first free page, we can confidently start at the
|
||||
// next page.
|
||||
//
|
||||
// However, s.searchAddr is not allowed to point into unmapped heap memory
|
||||
// unless it is maxSearchAddr, so make it the last page as opposed to
|
||||
// the page after.
|
||||
s.searchAddr = c.base + pageSize*(pageCachePages-1)
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -260,12 +260,13 @@ func TestPageAllocAllocToCache(t *testing.T) {
|
||||
if GOOS == "openbsd" && testing.Short() {
|
||||
t.Skip("skipping because virtual memory is limited; see #36210")
|
||||
}
|
||||
tests := map[string]struct {
|
||||
type test struct {
|
||||
before map[ChunkIdx][]BitRange
|
||||
scav map[ChunkIdx][]BitRange
|
||||
hits []PageCache // expected base addresses and patterns
|
||||
after map[ChunkIdx][]BitRange
|
||||
}{
|
||||
}
|
||||
tests := map[string]test{
|
||||
"AllFree": {
|
||||
before: map[ChunkIdx][]BitRange{
|
||||
BaseChunkIdx: {},
|
||||
@@ -349,6 +350,34 @@ func TestPageAllocAllocToCache(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
if PageAlloc64Bit != 0 {
|
||||
const chunkIdxBigJump = 0x100000 // chunk index offset which translates to O(TiB)
|
||||
|
||||
// This test is similar to the one with the same name for
|
||||
// pageAlloc.alloc and serves the same purpose.
|
||||
// See mpagealloc_test.go for details.
|
||||
sumsPerPhysPage := ChunkIdx(PhysPageSize / PallocSumBytes)
|
||||
baseChunkIdx := BaseChunkIdx &^ (sumsPerPhysPage - 1)
|
||||
tests["DiscontiguousMappedSumBoundary"] = test{
|
||||
before: map[ChunkIdx][]BitRange{
|
||||
baseChunkIdx + sumsPerPhysPage - 1: {{0, PallocChunkPages - 1}},
|
||||
baseChunkIdx + chunkIdxBigJump: {{1, PallocChunkPages - 1}},
|
||||
},
|
||||
scav: map[ChunkIdx][]BitRange{
|
||||
baseChunkIdx + sumsPerPhysPage - 1: {},
|
||||
baseChunkIdx + chunkIdxBigJump: {},
|
||||
},
|
||||
hits: []PageCache{
|
||||
NewPageCache(PageBase(baseChunkIdx+sumsPerPhysPage-1, PallocChunkPages-64), 1<<63, 0),
|
||||
NewPageCache(PageBase(baseChunkIdx+chunkIdxBigJump, 0), 1, 0),
|
||||
NewPageCache(0, 0, 0),
|
||||
},
|
||||
after: map[ChunkIdx][]BitRange{
|
||||
baseChunkIdx + sumsPerPhysPage - 1: {{0, PallocChunkPages}},
|
||||
baseChunkIdx + chunkIdxBigJump: {{0, PallocChunkPages}},
|
||||
},
|
||||
}
|
||||
}
|
||||
for name, v := range tests {
|
||||
v := v
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"runtime/internal/atomic"
|
||||
"runtime/internal/sys"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -479,7 +480,21 @@ func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32
|
||||
func getpid() int
|
||||
func tgkill(tgid, tid, sig int)
|
||||
|
||||
// touchStackBeforeSignal stores an errno value. If non-zero, it means
|
||||
// that we should touch the signal stack before sending a signal.
|
||||
// This is used on systems that have a bug when the signal stack must
|
||||
// be faulted in. See #35777 and #37436.
|
||||
//
|
||||
// This is accessed atomically as it is set and read in different threads.
|
||||
//
|
||||
// TODO(austin): Remove this after Go 1.15 when we remove the
|
||||
// mlockGsignal workaround.
|
||||
var touchStackBeforeSignal uint32
|
||||
|
||||
// signalM sends a signal to mp.
|
||||
func signalM(mp *m, sig int) {
|
||||
if atomic.Load(&touchStackBeforeSignal) != 0 {
|
||||
atomic.Cas((*uint32)(unsafe.Pointer(mp.gsignal.stack.hi-4)), 0, 0)
|
||||
}
|
||||
tgkill(getpid(), int(mp.procid), sig)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
package runtime
|
||||
|
||||
import "runtime/internal/atomic"
|
||||
|
||||
//go:noescape
|
||||
func uname(utsname *new_utsname) int
|
||||
|
||||
@@ -58,17 +60,34 @@ func osArchInit() {
|
||||
if m0.gsignal != nil {
|
||||
throw("gsignal quirk too late")
|
||||
}
|
||||
throwReportQuirk = throwBadKernel
|
||||
}
|
||||
}
|
||||
|
||||
func mlockGsignal(gsignal *g) {
|
||||
if err := mlock(gsignal.stack.hi-physPageSize, physPageSize); err < 0 {
|
||||
printlock()
|
||||
println("runtime: mlock of signal stack failed:", -err)
|
||||
if err == -_ENOMEM {
|
||||
println("runtime: increase the mlock limit (ulimit -l) or")
|
||||
}
|
||||
println("runtime: update your kernel to 5.3.15+, 5.4.2+, or 5.5+")
|
||||
throw("mlock failed")
|
||||
if atomic.Load(&touchStackBeforeSignal) != 0 {
|
||||
// mlock has already failed, don't try again.
|
||||
return
|
||||
}
|
||||
|
||||
// This mlock call may fail, but we don't report the failure.
|
||||
// Instead, if something goes badly wrong, we rely on prepareSignalM
|
||||
// and throwBadKernel to do further mitigation and to report a problem
|
||||
// to the user if mitigation fails. This is because many
|
||||
// systems have a limit on the total mlock size, and many kernels
|
||||
// that appear to have bad versions are actually patched to avoid the
|
||||
// bug described above. We want Go 1.14 to run on those systems.
|
||||
// See #37436.
|
||||
if errno := mlock(gsignal.stack.hi-physPageSize, physPageSize); errno < 0 {
|
||||
atomic.Store(&touchStackBeforeSignal, uint32(-errno))
|
||||
}
|
||||
}
|
||||
|
||||
// throwBadKernel is called, via throwReportQuirk, by throw.
|
||||
func throwBadKernel() {
|
||||
if errno := atomic.Load(&touchStackBeforeSignal); errno != 0 {
|
||||
println("runtime: note: your Linux kernel may be buggy")
|
||||
println("runtime: note: see https://golang.org/wiki/LinuxKernelSignalVectorBug")
|
||||
println("runtime: note: mlock workaround for kernel bug failed with errno", errno)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,9 +294,7 @@ func loadOptionalSyscalls() {
|
||||
|
||||
func monitorSuspendResume() {
|
||||
const (
|
||||
_DEVICE_NOTIFY_CALLBACK = 2
|
||||
_ERROR_FILE_NOT_FOUND = 2
|
||||
_ERROR_INVALID_PARAMETERS = 87
|
||||
_DEVICE_NOTIFY_CALLBACK = 2
|
||||
)
|
||||
type _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS struct {
|
||||
callback uintptr
|
||||
@@ -323,25 +321,8 @@ func monitorSuspendResume() {
|
||||
callback: compileCallback(*efaceOf(&fn), true),
|
||||
}
|
||||
handle := uintptr(0)
|
||||
ret := stdcall3(powerRegisterSuspendResumeNotification, _DEVICE_NOTIFY_CALLBACK,
|
||||
stdcall3(powerRegisterSuspendResumeNotification, _DEVICE_NOTIFY_CALLBACK,
|
||||
uintptr(unsafe.Pointer(¶ms)), uintptr(unsafe.Pointer(&handle)))
|
||||
// This function doesn't use GetLastError(), so we use the return value directly.
|
||||
switch ret {
|
||||
case 0:
|
||||
return // Successful, nothing more to do.
|
||||
case _ERROR_FILE_NOT_FOUND:
|
||||
// Systems without access to the suspend/resume notifier
|
||||
// also have their clock on "program time", and therefore
|
||||
// don't want or need this anyway.
|
||||
return
|
||||
case _ERROR_INVALID_PARAMETERS:
|
||||
// This is seen when running in Windows Docker.
|
||||
// See issue 36557.
|
||||
return
|
||||
default:
|
||||
println("runtime: PowerRegisterSuspendResumeNotification failed with errno=", ret)
|
||||
throw("runtime: PowerRegisterSuspendResumeNotification failure")
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
|
||||
@@ -216,7 +216,8 @@ func panicmem() {
|
||||
// The compiler turns a defer statement into a call to this.
|
||||
//go:nosplit
|
||||
func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
|
||||
if getg().m.curg != getg() {
|
||||
gp := getg()
|
||||
if gp.m.curg != gp {
|
||||
// go code on the system stack can't defer
|
||||
throw("defer on system stack")
|
||||
}
|
||||
@@ -234,6 +235,8 @@ func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
|
||||
if d._panic != nil {
|
||||
throw("deferproc: d.panic != nil after newdefer")
|
||||
}
|
||||
d.link = gp._defer
|
||||
gp._defer = d
|
||||
d.fn = fn
|
||||
d.pc = callerpc
|
||||
d.sp = sp
|
||||
@@ -374,7 +377,8 @@ func init() {
|
||||
}
|
||||
|
||||
// Allocate a Defer, usually using per-P pool.
|
||||
// Each defer must be released with freedefer.
|
||||
// Each defer must be released with freedefer. The defer is not
|
||||
// added to any defer chain yet.
|
||||
//
|
||||
// This must not grow the stack because there may be a frame without
|
||||
// stack map information when this is called.
|
||||
@@ -424,8 +428,6 @@ func newdefer(siz int32) *_defer {
|
||||
}
|
||||
d.siz = siz
|
||||
d.heap = true
|
||||
d.link = gp._defer
|
||||
gp._defer = d
|
||||
return d
|
||||
}
|
||||
|
||||
@@ -1003,11 +1005,12 @@ func gopanic(e interface{}) {
|
||||
atomic.Xadd(&runningPanicDefers, -1)
|
||||
|
||||
if done {
|
||||
// Remove any remaining non-started, open-coded defer
|
||||
// entry after a recover (there's at most one, if we just
|
||||
// ran a non-open-coded defer), since the entry will
|
||||
// become out-dated and the defer will be executed
|
||||
// normally.
|
||||
// Remove any remaining non-started, open-coded
|
||||
// defer entries after a recover, since the
|
||||
// corresponding defers will be executed normally
|
||||
// (inline). Any such entry will become stale once
|
||||
// we run the corresponding defers inline and exit
|
||||
// the associated stack frame.
|
||||
d := gp._defer
|
||||
var prev *_defer
|
||||
for d != nil {
|
||||
@@ -1025,8 +1028,9 @@ func gopanic(e interface{}) {
|
||||
} else {
|
||||
prev.link = d.link
|
||||
}
|
||||
newd := d.link
|
||||
freedefer(d)
|
||||
break
|
||||
d = newd
|
||||
} else {
|
||||
prev = d
|
||||
d = d.link
|
||||
@@ -1279,6 +1283,12 @@ func startpanic_m() bool {
|
||||
}
|
||||
}
|
||||
|
||||
// throwReportQuirk, if non-nil, is called by throw after dumping the stacks.
|
||||
//
|
||||
// TODO(austin): Remove this after Go 1.15 when we remove the
|
||||
// mlockGsignal workaround.
|
||||
var throwReportQuirk func()
|
||||
|
||||
var didothers bool
|
||||
var deadlock mutex
|
||||
|
||||
@@ -1325,6 +1335,10 @@ func dopanic_m(gp *g, pc, sp uintptr) bool {
|
||||
|
||||
printDebugLog()
|
||||
|
||||
if throwReportQuirk != nil {
|
||||
throwReportQuirk()
|
||||
}
|
||||
|
||||
return docrash
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,8 @@ Search:
|
||||
if len(m.freeStk) < len(stk) {
|
||||
m.freeStk = make([]uintptr, 1024)
|
||||
}
|
||||
e.stk = m.freeStk[:len(stk)]
|
||||
// Limit cap to prevent append from clobbering freeStk.
|
||||
e.stk = m.freeStk[:len(stk):len(stk)]
|
||||
m.freeStk = m.freeStk[len(stk):]
|
||||
|
||||
for j := range stk {
|
||||
|
||||
@@ -1172,16 +1172,37 @@ func TestTryAdd(t *testing.T) {
|
||||
{Value: []int64{20, 20 * period}, Location: []*profile.Location{{ID: 1}}},
|
||||
},
|
||||
}, {
|
||||
name: "recursive_inlined_funcs",
|
||||
name: "bug38096",
|
||||
input: []uint64{
|
||||
3, 0, 500, // hz = 500. Must match the period.
|
||||
// count (data[2]) == 0 && len(stk) == 1 is an overflow
|
||||
// entry. The "stk" entry is actually the count.
|
||||
4, 0, 0, 4242,
|
||||
},
|
||||
wantLocs: [][]string{{"runtime/pprof.lostProfileEvent"}},
|
||||
wantSamples: []*profile.Sample{
|
||||
{Value: []int64{4242, 4242 * period}, Location: []*profile.Location{{ID: 1}}},
|
||||
},
|
||||
}, {
|
||||
// If a function is called recursively then it must not be
|
||||
// inlined in the caller.
|
||||
//
|
||||
// N.B. We're generating an impossible profile here, with a
|
||||
// recursive inlineCallee call. This is simulating a non-Go
|
||||
// function that looks like an inlined Go function other than
|
||||
// its recursive property. See pcDeck.tryAdd.
|
||||
name: "recursive_func_is_not_inlined",
|
||||
input: []uint64{
|
||||
3, 0, 500, // hz = 500. Must match the period.
|
||||
5, 0, 30, inlinedCalleePtr, inlinedCalleePtr,
|
||||
4, 0, 40, inlinedCalleePtr,
|
||||
},
|
||||
wantLocs: [][]string{{"runtime/pprof.inlinedCallee"}},
|
||||
// inlinedCaller shows up here because
|
||||
// runtime_expandFinalInlineFrame adds it to the stack frame.
|
||||
wantLocs: [][]string{{"runtime/pprof.inlinedCallee"}, {"runtime/pprof.inlinedCaller"}},
|
||||
wantSamples: []*profile.Sample{
|
||||
{Value: []int64{30, 30 * period}, Location: []*profile.Location{{ID: 1}, {ID: 1}}},
|
||||
{Value: []int64{40, 40 * period}, Location: []*profile.Location{{ID: 1}}},
|
||||
{Value: []int64{30, 30 * period}, Location: []*profile.Location{{ID: 1}, {ID: 1}, {ID: 2}}},
|
||||
{Value: []int64{40, 40 * period}, Location: []*profile.Location{{ID: 1}, {ID: 2}}},
|
||||
},
|
||||
}, {
|
||||
name: "truncated_stack_trace_later",
|
||||
@@ -1202,12 +1223,36 @@ func TestTryAdd(t *testing.T) {
|
||||
4, 0, 70, inlinedCalleePtr,
|
||||
5, 0, 80, inlinedCalleePtr, inlinedCallerPtr,
|
||||
},
|
||||
wantLocs: [][]string{ // the inline info is screwed up, but better than a crash.
|
||||
{"runtime/pprof.inlinedCallee"},
|
||||
wantLocs: [][]string{{"runtime/pprof.inlinedCallee", "runtime/pprof.inlinedCaller"}},
|
||||
wantSamples: []*profile.Sample{
|
||||
{Value: []int64{70, 70 * period}, Location: []*profile.Location{{ID: 1}}},
|
||||
{Value: []int64{80, 80 * period}, Location: []*profile.Location{{ID: 1}}},
|
||||
},
|
||||
}, {
|
||||
// We can recover the inlined caller from a truncated stack.
|
||||
name: "truncated_stack_trace_only",
|
||||
input: []uint64{
|
||||
3, 0, 500, // hz = 500. Must match the period.
|
||||
4, 0, 70, inlinedCalleePtr,
|
||||
},
|
||||
wantLocs: [][]string{{"runtime/pprof.inlinedCallee", "runtime/pprof.inlinedCaller"}},
|
||||
wantSamples: []*profile.Sample{
|
||||
{Value: []int64{70, 70 * period}, Location: []*profile.Location{{ID: 1}}},
|
||||
},
|
||||
}, {
|
||||
// The same location is used for duplicated stacks.
|
||||
name: "truncated_stack_trace_twice",
|
||||
input: []uint64{
|
||||
3, 0, 500, // hz = 500. Must match the period.
|
||||
4, 0, 70, inlinedCalleePtr,
|
||||
5, 0, 80, inlinedCallerPtr, inlinedCalleePtr,
|
||||
},
|
||||
wantLocs: [][]string{
|
||||
{"runtime/pprof.inlinedCallee", "runtime/pprof.inlinedCaller"},
|
||||
{"runtime/pprof.inlinedCaller"}},
|
||||
wantSamples: []*profile.Sample{
|
||||
{Value: []int64{70, 70 * period}, Location: []*profile.Location{{ID: 1}}},
|
||||
{Value: []int64{80, 80 * period}, Location: []*profile.Location{{ID: 1}, {ID: 2}}},
|
||||
{Value: []int64{80, 80 * period}, Location: []*profile.Location{{ID: 2}, {ID: 1}}},
|
||||
},
|
||||
}}
|
||||
|
||||
|
||||
@@ -322,7 +322,10 @@ func (b *profileBuilder) addCPUData(data []uint64, tags []unsafe.Pointer) error
|
||||
// overflow record
|
||||
count = uint64(stk[0])
|
||||
stk = []uint64{
|
||||
uint64(funcPC(lostProfileEvent)),
|
||||
// gentraceback guarantees that PCs in the
|
||||
// stack can be unconditionally decremented and
|
||||
// still be valid, so we must do the same.
|
||||
uint64(funcPC(lostProfileEvent)+1),
|
||||
}
|
||||
}
|
||||
b.m.lookup(stk, tag).count += int64(count)
|
||||
@@ -384,6 +387,10 @@ func (b *profileBuilder) build() {
|
||||
// It may emit to b.pb, so there must be no message encoding in progress.
|
||||
func (b *profileBuilder) appendLocsForStack(locs []uint64, stk []uintptr) (newLocs []uint64) {
|
||||
b.deck.reset()
|
||||
|
||||
// The last frame might be truncated. Recover lost inline frames.
|
||||
stk = runtime_expandFinalInlineFrame(stk)
|
||||
|
||||
for len(stk) > 0 {
|
||||
addr := stk[0]
|
||||
if l, ok := b.locs[addr]; ok {
|
||||
@@ -395,22 +402,12 @@ func (b *profileBuilder) appendLocsForStack(locs []uint64, stk []uintptr) (newLo
|
||||
// then, record the cached location.
|
||||
locs = append(locs, l.id)
|
||||
|
||||
// The stk may be truncated due to the stack depth limit
|
||||
// (e.g. See maxStack and maxCPUProfStack in runtime) or
|
||||
// bugs in runtime. Avoid the crash in either case.
|
||||
// TODO(hyangah): The correct fix may require using the exact
|
||||
// pcs as the key for b.locs cache management instead of just
|
||||
// relying on the very first pc. We are late in the go1.14 dev
|
||||
// cycle, so this is a workaround with little code change.
|
||||
if len(l.pcs) > len(stk) {
|
||||
stk = nil
|
||||
// TODO(hyangah): would be nice if we can enable
|
||||
// debug print out on demand and report the problematic
|
||||
// cached location entry and stack traces. Do we already
|
||||
// have such facility to utilize (e.g. GODEBUG)?
|
||||
} else {
|
||||
stk = stk[len(l.pcs):] // skip the matching pcs.
|
||||
}
|
||||
// Skip the matching pcs.
|
||||
//
|
||||
// Even if stk was truncated due to the stack depth
|
||||
// limit, expandFinalInlineFrame above has already
|
||||
// fixed the truncation, ensuring it is long enough.
|
||||
stk = stk[len(l.pcs):]
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -427,9 +424,9 @@ func (b *profileBuilder) appendLocsForStack(locs []uint64, stk []uintptr) (newLo
|
||||
stk = stk[1:]
|
||||
continue
|
||||
}
|
||||
// add failed because this addr is not inlined with
|
||||
// the existing PCs in the deck. Flush the deck and retry to
|
||||
// handle this pc.
|
||||
// add failed because this addr is not inlined with the
|
||||
// existing PCs in the deck. Flush the deck and retry handling
|
||||
// this pc.
|
||||
if id := b.emitLocation(); id > 0 {
|
||||
locs = append(locs, id)
|
||||
}
|
||||
@@ -463,8 +460,8 @@ func (b *profileBuilder) appendLocsForStack(locs []uint64, stk []uintptr) (newLo
|
||||
// the fake pcs and restore the inlined and entry functions. Inlined functions
|
||||
// have the following properties:
|
||||
// Frame's Func is nil (note: also true for non-Go functions), and
|
||||
// Frame's Entry matches its entry function frame's Entry. (note: could also be true for recursive calls and non-Go functions),
|
||||
// Frame's Name does not match its entry function frame's name.
|
||||
// Frame's Entry matches its entry function frame's Entry (note: could also be true for recursive calls and non-Go functions), and
|
||||
// Frame's Name does not match its entry function frame's name (note: inlined functions cannot be recursive).
|
||||
//
|
||||
// As reading and processing the pcs in a stack trace one by one (from leaf to the root),
|
||||
// we use pcDeck to temporarily hold the observed pcs and their expanded frames
|
||||
@@ -486,8 +483,8 @@ func (d *pcDeck) reset() {
|
||||
// to the deck. If it fails the caller needs to flush the deck and retry.
|
||||
func (d *pcDeck) tryAdd(pc uintptr, frames []runtime.Frame, symbolizeResult symbolizeFlag) (success bool) {
|
||||
if existing := len(d.pcs); existing > 0 {
|
||||
// 'frames' are all expanded from one 'pc' and represent all inlined functions
|
||||
// so we check only the last one.
|
||||
// 'd.frames' are all expanded from one 'pc' and represent all
|
||||
// inlined functions so we check only the last one.
|
||||
newFrame := frames[0]
|
||||
last := d.frames[existing-1]
|
||||
if last.Func != nil { // the last frame can't be inlined. Flush.
|
||||
|
||||
@@ -422,3 +422,16 @@ func TestFakeMapping(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the profiler can handle an empty stack trace.
|
||||
// See issue 37967.
|
||||
func TestEmptyStack(t *testing.T) {
|
||||
b := []uint64{
|
||||
3, 0, 500, // hz = 500
|
||||
3, 0, 10, // 10 samples with an empty stack trace
|
||||
}
|
||||
_, err := translateCPUProfile(b)
|
||||
if err != nil {
|
||||
t.Fatalf("translating profile: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// runtime_expandFinalInlineFrame is defined in runtime/symtab.go.
|
||||
func runtime_expandFinalInlineFrame(stk []uintptr) []uintptr
|
||||
|
||||
// runtime_setProfLabel is defined in runtime/proflabel.go.
|
||||
func runtime_setProfLabel(labels unsafe.Pointer)
|
||||
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
#ifdef GOOS_darwin
|
||||
VZEROUPPER
|
||||
#endif
|
||||
PUSHQ BP
|
||||
MOVQ SP, BP
|
||||
// Save flags before clobbering them
|
||||
@@ -15,6 +12,11 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
ADJSP $368
|
||||
// But vet doesn't know ADJSP, so suppress vet stack checking
|
||||
NOP SP
|
||||
#ifdef GOOS_darwin
|
||||
CMPB internal∕cpu·X86+const_offsetX86HasAVX(SB), $0
|
||||
JE 2(PC)
|
||||
VZEROUPPER
|
||||
#endif
|
||||
MOVQ AX, 0(SP)
|
||||
MOVQ CX, 8(SP)
|
||||
MOVQ DX, 16(SP)
|
||||
|
||||
@@ -37,6 +37,7 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
MOVV R1, 208(R29)
|
||||
MOVV LO, R1
|
||||
MOVV R1, 216(R29)
|
||||
#ifndef GOMIPS64_softfloat
|
||||
MOVV FCR31, R1
|
||||
MOVV R1, 224(R29)
|
||||
MOVD F0, 232(R29)
|
||||
@@ -71,7 +72,9 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
MOVD F29, 464(R29)
|
||||
MOVD F30, 472(R29)
|
||||
MOVD F31, 480(R29)
|
||||
#endif
|
||||
CALL ·asyncPreempt2(SB)
|
||||
#ifndef GOMIPS64_softfloat
|
||||
MOVD 480(R29), F31
|
||||
MOVD 472(R29), F30
|
||||
MOVD 464(R29), F29
|
||||
@@ -106,6 +109,7 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
MOVD 232(R29), F0
|
||||
MOVV 224(R29), R1
|
||||
MOVV R1, FCR31
|
||||
#endif
|
||||
MOVV 216(R29), R1
|
||||
MOVV R1, LO
|
||||
MOVV 208(R29), R1
|
||||
|
||||
@@ -37,6 +37,7 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
MOVW R1, 104(R29)
|
||||
MOVW LO, R1
|
||||
MOVW R1, 108(R29)
|
||||
#ifndef GOMIPS_softfloat
|
||||
MOVW FCR31, R1
|
||||
MOVW R1, 112(R29)
|
||||
MOVF F0, 116(R29)
|
||||
@@ -71,7 +72,9 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
MOVF F29, 232(R29)
|
||||
MOVF F30, 236(R29)
|
||||
MOVF F31, 240(R29)
|
||||
#endif
|
||||
CALL ·asyncPreempt2(SB)
|
||||
#ifndef GOMIPS_softfloat
|
||||
MOVF 240(R29), F31
|
||||
MOVF 236(R29), F30
|
||||
MOVF 232(R29), F29
|
||||
@@ -106,6 +109,7 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
|
||||
MOVF 116(R29), F0
|
||||
MOVW 112(R29), R1
|
||||
MOVW R1, FCR31
|
||||
#endif
|
||||
MOVW 108(R29), R1
|
||||
MOVW R1, LO
|
||||
MOVW 104(R29), R1
|
||||
|
||||
@@ -1762,10 +1762,16 @@ func startTemplateThread() {
|
||||
if GOARCH == "wasm" { // no threads on wasm yet
|
||||
return
|
||||
}
|
||||
|
||||
// Disable preemption to guarantee that the template thread will be
|
||||
// created before a park once haveTemplateThread is set.
|
||||
mp := acquirem()
|
||||
if !atomic.Cas(&newmHandoff.haveTemplateThread, 0, 1) {
|
||||
releasem(mp)
|
||||
return
|
||||
}
|
||||
newm(templateThread, nil)
|
||||
releasem(mp)
|
||||
}
|
||||
|
||||
// templateThread is a thread in a known-good state that exists solely
|
||||
|
||||
@@ -6,6 +6,7 @@ package runtime_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"math"
|
||||
"net"
|
||||
"runtime"
|
||||
@@ -923,6 +924,29 @@ func TestLockOSThreadAvoidsStatePropagation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLockOSThreadTemplateThreadRace(t *testing.T) {
|
||||
testenv.MustHaveGoRun(t)
|
||||
|
||||
exe, err := buildTestProg(t, "testprog")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
iterations := 100
|
||||
if testing.Short() {
|
||||
// Reduce run time to ~100ms, with much lower probability of
|
||||
// catching issues.
|
||||
iterations = 5
|
||||
}
|
||||
for i := 0; i < iterations; i++ {
|
||||
want := "OK\n"
|
||||
output := runBuiltTestProg(t, exe, "LockOSThreadTemplateThreadRace")
|
||||
if output != want {
|
||||
t.Fatalf("run %d: want %q, got %q", i, want, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fakeSyscall emulates a system call.
|
||||
//go:nosplit
|
||||
func fakeSyscall(duration time.Duration) {
|
||||
|
||||
@@ -4,10 +4,10 @@ the LLVM project (https://github.com/llvm/llvm-project/tree/master/compiler-rt).
|
||||
|
||||
To update the .syso files use golang.org/x/build/cmd/racebuild.
|
||||
|
||||
race_darwin_amd64.syso built with LLVM 810ae8ddac890a6613d814c0b5415c7fcb7f5cca and Go 8c6876e9a481a2ea48070d3285a07163f564877b.
|
||||
race_freebsd_amd64.syso built with LLVM 810ae8ddac890a6613d814c0b5415c7fcb7f5cca and Go 8c6876e9a481a2ea48070d3285a07163f564877b.
|
||||
race_linux_amd64.syso built with LLVM 810ae8ddac890a6613d814c0b5415c7fcb7f5cca and Go 8c6876e9a481a2ea48070d3285a07163f564877b.
|
||||
race_linux_ppc64le.syso built with LLVM 810ae8ddac890a6613d814c0b5415c7fcb7f5cca and Go 8c6876e9a481a2ea48070d3285a07163f564877b.
|
||||
race_netbsd_amd64.syso built with LLVM 810ae8ddac890a6613d814c0b5415c7fcb7f5cca and Go 8c6876e9a481a2ea48070d3285a07163f564877b.
|
||||
race_windows_amd64.syso built with LLVM 810ae8ddac890a6613d814c0b5415c7fcb7f5cca and Go 8c6876e9a481a2ea48070d3285a07163f564877b.
|
||||
race_linux_arm64.syso built with LLVM 810ae8ddac890a6613d814c0b5415c7fcb7f5cca and Go 8c6876e9a481a2ea48070d3285a07163f564877b.
|
||||
race_darwin_amd64.syso built with LLVM 0fb8a5356214c47bbb832e89fbb3da1c755eeb73 and Go 95773ab9b053edc43ba07a182f3d5e0e29775a45.
|
||||
race_freebsd_amd64.syso built with LLVM 0fb8a5356214c47bbb832e89fbb3da1c755eeb73 and Go 95773ab9b053edc43ba07a182f3d5e0e29775a45.
|
||||
race_linux_amd64.syso built with LLVM 0fb8a5356214c47bbb832e89fbb3da1c755eeb73 and Go 95773ab9b053edc43ba07a182f3d5e0e29775a45.
|
||||
race_linux_ppc64le.syso built with LLVM 2db63723a87527bac797996b8aa9d2f5a176b2f7 and Go e31d741801514f2a008625fd246644d2214f4516.
|
||||
race_netbsd_amd64.syso built with LLVM efeb35e19563df911febe6a53151103c3b6011a5 and Go fd18f3ba5031079102ca4dc4cf425c2b496408ba.
|
||||
race_windows_amd64.syso built with LLVM 0fb8a5356214c47bbb832e89fbb3da1c755eeb73 and Go 95773ab9b053edc43ba07a182f3d5e0e29775a45.
|
||||
race_linux_arm64.syso built with LLVM 0fb8a5356214c47bbb832e89fbb3da1c755eeb73 and Go 95773ab9b053edc43ba07a182f3d5e0e29775a45.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user