[インデックス 12326] ファイルの概要
このコミットは、Go言語の標準ライブラリであるos
パッケージにおいて、Windows環境でのプロセスのユーザー時間(UserTime)とシステム時間(SystemTime)の取得を正確に実装するための変更です。具体的には、Windows APIのGetProcessTimes
関数をGoのsyscall
パッケージを通じて呼び出し、その結果をos.ProcessState
に反映させることで、これまで常に0を返していたこれらのメトリックが正しく報告されるようになります。
変更されたファイルは以下の通りです。
src/pkg/os/exec_windows.go
:os.Process
が終了した際に、プロセスの時間情報を取得し、ProcessState
に格納するロジックが追加されました。また、ProcessState
のuserTime()
とsystemTime()
メソッドが、取得した時間情報に基づいて正しい値を返すように修正されました。src/pkg/syscall/syscall_windows.go
: Windows APIのGetProcessTimes
関数のGo言語でのシグネチャが追加され、syscall.Rusage
構造体がプロセスの時間情報を保持できるように拡張されました。src/pkg/syscall/zsyscall_windows_386.go
: 32ビット(x86)アーキテクチャ向けのGetProcessTimes
システムコールラッパーの実装が追加されました。src/pkg/syscall/zsyscall_windows_amd64.go
: 64ビット(x64)アーキテクチャ向けのGetProcessTimes
システムコールラッパーの実装が追加されました。src/pkg/syscall/ztypes_windows.go
:Filetime
構造体のNanoseconds()
メソッドに関するコメントが追加されました。
コミット
commit c3fbc9a5e817d037f04abba4ec6d8a453afb344a
Author: Alex Brainman <alex.brainman@gmail.com>
Date: Fri Mar 2 14:47:40 2012 +1100
os: implement UserTime/SystemTime on windows
Fixes #3145.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5721044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c3fbc9a5e817d037f04abba4ec6d8a453afb344a
元コミット内容
os: implement UserTime/SystemTime on windows
Fixes #3145.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5721044
変更の背景
Go言語のos
パッケージは、実行中のプロセスに関する情報を提供する機能を持っています。特に、os.ProcessState
構造体は、プロセスが終了した後の状態(終了コード、CPU時間など)をカプセル化します。しかし、このコミットがなされる以前のWindows環境では、ProcessState
が提供するUserTime()
(ユーザーモードでのCPU時間)とSystemTime()
(カーネルモードでのCPU時間)のメソッドが常に0を返していました。これは、Windowsの基盤となるAPIからこれらの時間情報を適切に取得し、Goのデータ構造にマッピングする実装が欠けていたためです。
この問題は、GoのIssueトラッカーでIssue #3145として報告されていました。プロセスのCPU使用時間を正確に測定することは、パフォーマンス分析、リソース管理、およびシステム監視において非常に重要です。例えば、アプリケーションのボトルネックを特定したり、特定のプロセスがシステムリソースをどれだけ消費しているかを把握したりするために、これらのメトリックは不可欠です。このコミットは、Windows上でのGoアプリケーションのプロファイリングと監視能力を向上させることを目的としています。
前提知識の解説
このコミットの変更内容を理解するためには、以下の概念について知っておく必要があります。
-
Go言語の
os
パッケージ:- Goの標準ライブラリの一部で、オペレーティングシステムとのインタラクションを提供します。ファイルシステム操作、プロセス管理、環境変数へのアクセスなどが含まれます。
os.Process
: 実行中のプロセスを表す構造体です。os.ProcessState
:os.Process
が終了した後に、そのプロセスの最終状態(終了コード、CPU時間など)を保持する構造体です。UserTime()
とSystemTime()
メソッドを持ち、それぞれユーザーモードとシステムモードでのCPU時間を返します。
-
Go言語の
syscall
パッケージ:- オペレーティングシステムが提供する低レベルなシステムコールへのインターフェースを提供します。これにより、GoプログラムからOS固有の機能(例えば、Windows APIやLinuxの
prctl
など)を直接呼び出すことができます。 - このパッケージは、OSに依存する機能の実装によく使用されます。
- オペレーティングシステムが提供する低レベルなシステムコールへのインターフェースを提供します。これにより、GoプログラムからOS固有の機能(例えば、Windows APIやLinuxの
-
Windows API
GetProcessTimes
関数:- Windowsオペレーティングシステムが提供するAPI関数の一つで、指定されたプロセスのタイミング情報を取得するために使用されます。
- この関数は、以下の4つの
FILETIME
構造体へのポインタを引数として取ります。lpCreationTime
: プロセスが作成された時刻。lpExitTime
: プロセスが終了した時刻。lpKernelTime
: プロセスがカーネルモードで実行された時間。lpUserTime
: プロセスがユーザーモードで実行された時間。
- これらの時間は、プロセスがCPUを消費した合計時間を示します。
-
FILETIME
構造体 (Windows):- Windows APIで使用される時間表現の構造体です。
- 1601年1月1日(UTC)からの100ナノ秒間隔の数を64ビット整数で表します。
HighDateTime
(上位32ビット)とLowDateTime
(下位32ビット)の2つのDWORD
(32ビット符号なし整数)フィールドで構成されます。
-
Rusage
構造体 (Resource Usage):- 元々はUnix系のシステムでプロセスのリソース使用状況(CPU時間、メモリ使用量など)を報告するために使用される構造体です。
- Goの
syscall
パッケージでは、各OSの特性に合わせてこの概念を実装しています。このコミットでは、Windows固有のプロセス時間情報を保持するために拡張されました。
-
ユーザー時間(User Time)とシステム時間(System Time):
- ユーザー時間: プロセスがユーザーモードでCPU命令を実行するのに費やした時間です。アプリケーションコードの実行、ライブラリ関数の呼び出しなどがこれに該当します。
- システム時間(カーネル時間): プロセスがカーネルモードでCPU命令を実行するのに費やした時間です。システムコール(ファイルI/O、ネットワーク通信、メモリ割り当てなど)の実行、OSカーネルの処理などがこれに該当します。
- これらの時間は、プロセスの総CPU使用時間を構成します。
-
Go言語の
time.Duration
型:- Goの標準ライブラリ
time
パッケージで定義されている型で、時間の長さを表します。ナノ秒単位の整数として内部的に表現されます。
- Goの標準ライブラリ
技術的詳細
このコミットの技術的な核心は、Windows固有のプロセス時間取得APIであるGetProcessTimes
をGoのsyscall
パッケージを通じて利用し、その結果をos
パッケージのProcessState
に適切にマッピングすることにあります。
-
syscall.GetProcessTimes
の導入:src/pkg/syscall/syscall_windows.go
に、Windows APIのGetProcessTimes
に対応するGoの関数シグネチャが追加されました。これにより、GoプログラムからこのAPIを型安全に呼び出すことが可能になります。- 実際のシステムコールへのディスパッチは、アーキテクチャ固有のファイル(
zsyscall_windows_386.go
とzsyscall_windows_amd64.go
)で行われます。これらのファイルでは、Syscall6
関数(6つの引数を取るシステムコールを呼び出すためのGoの内部関数)を使用して、GetProcessTimes
のDLLエントリポイントを呼び出しています。
-
syscall.Rusage
構造体の拡張:src/pkg/syscall/syscall_windows.go
において、Rusage
構造体がWindowsのGetProcessTimes
が返す4つのFILETIME
フィールド(CreationTime
,ExitTime
,KernelTime
,UserTime
)を持つように変更されました。これにより、プロセス時間情報をGoの構造体として保持できるようになります。
-
os.Process.wait()
での時間情報取得:src/pkg/os/exec_windows.go
のProcess.wait()
メソッド(プロセスが終了するのを待ち、その状態を返すメソッド)内で、syscall.GetProcessTimes
が呼び出されるようになりました。- プロセスのハンドルと、
syscall.Rusage
構造体のアドレスを引数として渡し、GetProcessTimes
から返される時間情報をRusage
インスタンスに格納します。 - この
Rusage
インスタンスは、ProcessState
構造体の新しいフィールドとして渡されるようになります。
-
ftToDuration
ヘルパー関数の導入:src/pkg/os/exec_windows.go
にftToDuration
という新しいヘルパー関数が追加されました。- この関数は、
syscall.Filetime
構造体へのポインタを受け取り、それをGoのtime.Duration
型に変換します。 FILETIME
は100ナノ秒単位で時間を表現するため、ftToDuration
はFILETIME
の値を100倍し、さらにtime.Nanosecond
を掛けることで、正確なtime.Duration
値に変換します。
-
os.ProcessState.userTime()
とos.ProcessState.systemTime()
の実装:src/pkg/os/exec_windows.go
において、これまで常に0を返していたProcessState.userTime()
とProcessState.systemTime()
メソッドが、ProcessState
に格納されたRusage
構造体のUserTime
とKernelTime
フィールドをftToDuration
で変換した値を返すように修正されました。
これらの変更により、GoプログラムはWindows上で実行されるプロセスの正確なユーザー時間とシステム時間を取得できるようになり、Goのクロスプラットフォームなプロセス管理機能がWindowsにおいてもより完全なものとなりました。
コアとなるコードの変更箇所
src/pkg/os/exec_windows.go
// BUG(rsc): On Windows, ProcessState's UserTime and SystemTime methods always return 0.
+func ftToDuration(ft *syscall.Filetime) time.Duration {
+ n := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime) // in 100-nanosecond intervals
+ return time.Duration(n*100) * time.Nanosecond
+}
func (p *ProcessState) userTime() time.Duration {
- return 0
+ return ftToDuration(&p.rusage.UserTime)
}
func (p *ProcessState) systemTime() time.Duration {
- return 0
+ return ftToDuration(&p.rusage.KernelTime)
}
func (p *Process) wait() (ps *ProcessState, err error) {
// ... (既存のコード) ...
if e != nil {
return nil, NewSyscallError("GetExitCodeProcess", e)
}
+ var u syscall.Rusage
+ e = syscall.GetProcessTimes(syscall.Handle(p.handle), &u.CreationTime, &u.ExitTime, &u.KernelTime, &u.UserTime)
+ if e != nil {
+ return nil, NewSyscallError("GetProcessTimes", e)
+ }
p.done = true
defer p.Release()
- return &ProcessState{p.Pid, syscall.WaitStatus{Status: s, ExitCode: ec}, new(syscall.Rusage)}, nil
+ return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil
}
src/pkg/syscall/syscall_windows.go
//sys GetExitCodeProcess(handle Handle, exitcode *uint32) (err error)\n
//sys GetStartupInfo(startupInfo *StartupInfo) (err error) = GetStartupInfoW\n
//sys GetCurrentProcess() (pseudoHandle Handle, err error)\n
+//sys GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime, kernelTime *Filetime, userTime *Filetime) (err error)\n
//sys DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (err error)\n
//sys WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32, err error) [failretval==0xffffffff]\n
//sys GetTempPath(buflen uint32, buf *uint16) (n uint32, err error) = GetTempPathW\n
// Invented structures to support what package os expects.\n
-type Rusage struct{}\n
+type Rusage struct {\n
+\tCreationTime Filetime\n+\tExitTime Filetime\n+\tKernelTime Filetime\n+\tUserTime Filetime\n+}\n
コアとなるコードの解説
src/pkg/os/exec_windows.go
の変更点
-
ftToDuration
関数の追加:- この関数は、Windows APIの
FILETIME
構造体(syscall.Filetime
としてGoにマッピングされる)をGoのtime.Duration
型に変換するためのユーティリティです。 FILETIME
は100ナノ秒単位の64ビット整数で時間を表現するため、ft.HighDateTime
を32ビット左シフトしてft.LowDateTime
と結合することで、完全な64ビットのナノ秒間隔数を取得します。- その結果に100を掛け、さらに
time.Nanosecond
を掛けることで、正確なtime.Duration
値(ナノ秒単位)に変換しています。
- この関数は、Windows APIの
-
ProcessState.userTime()
とProcessState.systemTime()
の修正:- これらのメソッドは、
ProcessState
構造体内のrusage
フィールド(syscall.Rusage
型)から、それぞれUserTime
とKernelTime
(Windowsではシステム時間に対応)のFiletime
値を取り出します。 - 取り出した
Filetime
値を新しく追加されたftToDuration
関数に渡し、time.Duration
型に変換された正確なCPU時間を返します。これにより、Windows環境でもこれらのメソッドが意味のある値を返すようになりました。
- これらのメソッドは、
-
Process.wait()
メソッドの変更:- プロセスが終了し、その終了コードを取得した後、
syscall.Rusage
型の変数u
を宣言します。 syscall.GetProcessTimes
関数を呼び出し、プロセスのハンドルとu
の各時間フィールドへのポインタを渡します。これにより、プロセスの作成時間、終了時間、カーネル時間、ユーザー時間がu
に格納されます。GetProcessTimes
の呼び出しが失敗した場合は、適切なエラーを返します。- 最後に、
ProcessState
を構築する際に、これまでの空のsyscall.Rusage
インスタンスの代わりに、取得した時間情報が格納された&u
を渡すように変更されました。これにより、ProcessState
がプロセスの正確な時間情報を持つことができるようになります。
- プロセスが終了し、その終了コードを取得した後、
src/pkg/syscall/syscall_windows.go
の変更点
-
GetProcessTimes
システムコール定義の追加://sys
コメントは、Goのsyscall
パッケージがWindows API関数をGoの関数として公開するための特別なディレクティブです。//sys GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime, kernelTime *Filetime, userTime *Filetime) (err error)
という行が追加され、GoプログラムからGetProcessTimes
を呼び出す際のシグネチャが定義されました。
-
Rusage
構造体の拡張:syscall.Rusage
構造体が、WindowsのGetProcessTimes
が返す4つのFiletime
フィールド(CreationTime
,ExitTime
,KernelTime
,UserTime
)を含むように定義されました。これにより、Windows固有のプロセス時間情報をGoの型システム内で表現できるようになります。
これらの変更により、Goのos
パッケージはWindows環境でプロセスのCPU使用時間を正確に報告できるようになり、Goアプリケーションのパフォーマンス監視やプロファイリングの精度が向上しました。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/c3fbc9a5e817d037f04abba4ec6d8a453afb344a
- Go Issue #3145: https://golang.org/issue/3145
- Gerrit Change-ID: https://golang.org/cl/5721044
参考にした情報源リンク
- Microsoft Learn:
GetProcessTimes
function: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocesstimes - Microsoft Learn:
FILETIME
structure: https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime - Go
os
package documentation: https://pkg.go.dev/os - Go
syscall
package documentation: https://pkg.go.dev/syscall - Go
time
package documentation: https://pkg.go.dev/time