|
@@ -93,16 +93,33 @@ read_memory_as_slice :: proc(h: win32.HANDLE, addr: rawptr, dest: []$T) -> (byte
|
|
|
@(private="package")
|
|
|
_process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
|
|
info.pid = pid
|
|
|
- defer if err != nil {
|
|
|
- free_process_info(info, allocator)
|
|
|
+ // Note(flysand): Open the process handle right away to prevent some race
|
|
|
+ // conditions. Once the handle is open, the process will be kept alive by
|
|
|
+ // the OS.
|
|
|
+ ph := win32.INVALID_HANDLE_VALUE
|
|
|
+ if selection >= {.Command_Line, .Environment, .Working_Dir, .Username} {
|
|
|
+ ph = win32.OpenProcess(
|
|
|
+ win32.PROCESS_QUERY_LIMITED_INFORMATION | win32.PROCESS_VM_READ,
|
|
|
+ false,
|
|
|
+ u32(pid),
|
|
|
+ )
|
|
|
+ if ph == win32.INVALID_HANDLE_VALUE {
|
|
|
+ err = _get_platform_error()
|
|
|
+ return
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- // Data obtained from process snapshots
|
|
|
- if selection >= {.PPid, .Priority} {
|
|
|
+ defer if ph != win32.INVALID_HANDLE_VALUE {
|
|
|
+ win32.CloseHandle(ph)
|
|
|
+ }
|
|
|
+ snapshot_process: if selection >= {.PPid, .Priority} {
|
|
|
entry, entry_err := _process_entry_by_pid(info.pid)
|
|
|
if entry_err != nil {
|
|
|
- err = General_Error.Not_Exist
|
|
|
- return
|
|
|
+ err = entry_err
|
|
|
+ if entry_err == General_Error.Not_Exist {
|
|
|
+ return
|
|
|
+ } else {
|
|
|
+ break snapshot_process
|
|
|
+ }
|
|
|
}
|
|
|
if .PPid in selection {
|
|
|
info.fields += {.PPid}
|
|
@@ -113,29 +130,18 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
|
|
|
info.priority = int(entry.pcPriClassBase)
|
|
|
}
|
|
|
}
|
|
|
- if .Executable_Path in selection { // snap module
|
|
|
- info.executable_path = _process_exe_by_pid(pid, allocator) or_return
|
|
|
- info.fields += {.Executable_Path}
|
|
|
- }
|
|
|
-
|
|
|
- ph := win32.INVALID_HANDLE_VALUE
|
|
|
-
|
|
|
- if selection >= {.Command_Line, .Environment, .Working_Dir, .Username} { // need process handle
|
|
|
- ph = win32.OpenProcess(
|
|
|
- win32.PROCESS_QUERY_LIMITED_INFORMATION | win32.PROCESS_VM_READ,
|
|
|
- false,
|
|
|
- u32(pid),
|
|
|
- )
|
|
|
- if ph == win32.INVALID_HANDLE_VALUE {
|
|
|
- err = _get_platform_error()
|
|
|
+ snapshot_modules: if .Executable_Path in selection {
|
|
|
+ exe_path: string
|
|
|
+ exe_path, err = _process_exe_by_pid(pid, allocator)
|
|
|
+ if _, ok := err.(runtime.Allocator_Error); ok {
|
|
|
return
|
|
|
+ } else if err != nil {
|
|
|
+ break snapshot_modules
|
|
|
}
|
|
|
+ info.executable_path = exe_path
|
|
|
+ info.fields += {.Executable_Path}
|
|
|
}
|
|
|
- defer if ph != win32.INVALID_HANDLE_VALUE {
|
|
|
- win32.CloseHandle(ph)
|
|
|
- }
|
|
|
-
|
|
|
- if selection >= {.Command_Line, .Environment, .Working_Dir} { // need peb
|
|
|
+ read_peb: if selection >= {.Command_Line, .Environment, .Working_Dir} {
|
|
|
process_info_size: u32
|
|
|
process_info: win32.PROCESS_BASIC_INFORMATION
|
|
|
status := win32.NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size)
|
|
@@ -143,25 +149,26 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
|
|
|
// TODO(flysand): There's probably a mismatch between NTSTATUS and
|
|
|
// windows userland error codes, I haven't checked.
|
|
|
err = Platform_Error(status)
|
|
|
- return
|
|
|
- }
|
|
|
- if process_info.PebBaseAddress == nil {
|
|
|
- // Not sure what the error is
|
|
|
- err = General_Error.Unsupported
|
|
|
- return
|
|
|
+ break read_peb
|
|
|
}
|
|
|
+ assert(process_info.PebBaseAddress != nil)
|
|
|
process_peb: win32.PEB
|
|
|
-
|
|
|
- _ = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb) or_return
|
|
|
-
|
|
|
+ _, err = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb)
|
|
|
+ if err != nil {
|
|
|
+ break read_peb
|
|
|
+ }
|
|
|
process_params: win32.RTL_USER_PROCESS_PARAMETERS
|
|
|
- _ = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params) or_return
|
|
|
-
|
|
|
+ _, err = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params)
|
|
|
+ if err != nil {
|
|
|
+ break read_peb
|
|
|
+ }
|
|
|
if selection >= {.Command_Line, .Command_Args} {
|
|
|
TEMP_ALLOCATOR_GUARD()
|
|
|
cmdline_w := make([]u16, process_params.CommandLine.Length, temp_allocator()) or_return
|
|
|
- _ = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w) or_return
|
|
|
-
|
|
|
+ _, err = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w)
|
|
|
+ if err != nil {
|
|
|
+ break read_peb
|
|
|
+ }
|
|
|
if .Command_Line in selection {
|
|
|
info.command_line = win32_utf16_to_utf8(cmdline_w, allocator) or_return
|
|
|
info.fields += {.Command_Line}
|
|
@@ -175,23 +182,33 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
|
|
|
TEMP_ALLOCATOR_GUARD()
|
|
|
env_len := process_params.EnvironmentSize / 2
|
|
|
envs_w := make([]u16, env_len, temp_allocator()) or_return
|
|
|
- _ = read_memory_as_slice(ph, process_params.Environment, envs_w) or_return
|
|
|
-
|
|
|
+ _, err = read_memory_as_slice(ph, process_params.Environment, envs_w)
|
|
|
+ if err != nil {
|
|
|
+ break read_peb
|
|
|
+ }
|
|
|
info.environment = _parse_environment_block(raw_data(envs_w), allocator) or_return
|
|
|
info.fields += {.Environment}
|
|
|
}
|
|
|
if .Working_Dir in selection {
|
|
|
TEMP_ALLOCATOR_GUARD()
|
|
|
cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator()) or_return
|
|
|
- _ = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w) or_return
|
|
|
-
|
|
|
+ _, err = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w)
|
|
|
+ if err != nil {
|
|
|
+ break read_peb
|
|
|
+ }
|
|
|
info.working_dir = win32_utf16_to_utf8(cwd_w, allocator) or_return
|
|
|
info.fields += {.Working_Dir}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- if .Username in selection {
|
|
|
- info.username = _get_process_user(ph, allocator) or_return
|
|
|
+ read_username: if .Username in selection {
|
|
|
+ username: string
|
|
|
+ username, err = _get_process_user(ph, allocator)
|
|
|
+ if _, ok := err.(runtime.Allocator_Error); ok {
|
|
|
+ return
|
|
|
+ } else if err != nil {
|
|
|
+ break read_username
|
|
|
+ }
|
|
|
+ info.username = username
|
|
|
info.fields += {.Username}
|
|
|
}
|
|
|
err = nil
|
|
@@ -202,16 +219,16 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
|
|
|
_process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
|
|
pid := process.pid
|
|
|
info.pid = pid
|
|
|
- defer if err != nil {
|
|
|
- free_process_info(info, allocator)
|
|
|
- }
|
|
|
-
|
|
|
// Data obtained from process snapshots
|
|
|
- if selection >= {.PPid, .Priority} { // snap process
|
|
|
+ snapshot_process: if selection >= {.PPid, .Priority} {
|
|
|
entry, entry_err := _process_entry_by_pid(info.pid)
|
|
|
if entry_err != nil {
|
|
|
- err = General_Error.Not_Exist
|
|
|
- return
|
|
|
+ err = entry_err
|
|
|
+ if entry_err == General_Error.Not_Exist {
|
|
|
+ return
|
|
|
+ } else {
|
|
|
+ break snapshot_process
|
|
|
+ }
|
|
|
}
|
|
|
if .PPid in selection {
|
|
|
info.fields += {.PPid}
|
|
@@ -222,12 +239,19 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
|
|
|
info.priority = int(entry.pcPriClassBase)
|
|
|
}
|
|
|
}
|
|
|
- if .Executable_Path in selection { // snap module
|
|
|
- info.executable_path = _process_exe_by_pid(pid, allocator) or_return
|
|
|
+ snapshot_module: if .Executable_Path in selection {
|
|
|
+ exe_path: string
|
|
|
+ exe_path, err = _process_exe_by_pid(pid, allocator)
|
|
|
+ if _, ok := err.(runtime.Allocator_Error); ok {
|
|
|
+ return
|
|
|
+ } else if err != nil {
|
|
|
+ break snapshot_module
|
|
|
+ }
|
|
|
+ info.executable_path = exe_path
|
|
|
info.fields += {.Executable_Path}
|
|
|
}
|
|
|
ph := win32.HANDLE(process.handle)
|
|
|
- if selection >= {.Command_Line, .Environment, .Working_Dir} { // need peb
|
|
|
+ read_peb: if selection >= {.Command_Line, .Environment, .Working_Dir} {
|
|
|
process_info_size: u32
|
|
|
process_info: win32.PROCESS_BASIC_INFORMATION
|
|
|
status := win32.NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size)
|
|
@@ -237,23 +261,24 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
|
|
|
err = Platform_Error(status)
|
|
|
return
|
|
|
}
|
|
|
- if process_info.PebBaseAddress == nil {
|
|
|
- // Not sure what the error is
|
|
|
- err = General_Error.Unsupported
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
+ assert(process_info.PebBaseAddress != nil)
|
|
|
process_peb: win32.PEB
|
|
|
- _ = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb) or_return
|
|
|
-
|
|
|
+ _, err = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb)
|
|
|
+ if err != nil {
|
|
|
+ break read_peb
|
|
|
+ }
|
|
|
process_params: win32.RTL_USER_PROCESS_PARAMETERS
|
|
|
- _ = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params) or_return
|
|
|
-
|
|
|
+ _, err = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params)
|
|
|
+ if err != nil {
|
|
|
+ break read_peb
|
|
|
+ }
|
|
|
if selection >= {.Command_Line, .Command_Args} {
|
|
|
TEMP_ALLOCATOR_GUARD()
|
|
|
cmdline_w := make([]u16, process_params.CommandLine.Length, temp_allocator()) or_return
|
|
|
- _ = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w) or_return
|
|
|
-
|
|
|
+ _, err = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w)
|
|
|
+ if err != nil {
|
|
|
+ break read_peb
|
|
|
+ }
|
|
|
if .Command_Line in selection {
|
|
|
info.command_line = win32_utf16_to_utf8(cmdline_w, allocator) or_return
|
|
|
info.fields += {.Command_Line}
|
|
@@ -263,28 +288,37 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
|
|
|
info.fields += {.Command_Args}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
if .Environment in selection {
|
|
|
TEMP_ALLOCATOR_GUARD()
|
|
|
env_len := process_params.EnvironmentSize / 2
|
|
|
envs_w := make([]u16, env_len, temp_allocator()) or_return
|
|
|
- _ = read_memory_as_slice(ph, process_params.Environment, envs_w) or_return
|
|
|
-
|
|
|
+ _, err = read_memory_as_slice(ph, process_params.Environment, envs_w)
|
|
|
+ if err != nil {
|
|
|
+ break read_peb
|
|
|
+ }
|
|
|
info.environment = _parse_environment_block(raw_data(envs_w), allocator) or_return
|
|
|
info.fields += {.Environment}
|
|
|
}
|
|
|
-
|
|
|
if .Working_Dir in selection {
|
|
|
TEMP_ALLOCATOR_GUARD()
|
|
|
cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator()) or_return
|
|
|
- _ = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w) or_return
|
|
|
-
|
|
|
+ _, err = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w)
|
|
|
+ if err != nil {
|
|
|
+ break read_peb
|
|
|
+ }
|
|
|
info.working_dir = win32_utf16_to_utf8(cwd_w, allocator) or_return
|
|
|
info.fields += {.Working_Dir}
|
|
|
}
|
|
|
}
|
|
|
- if .Username in selection {
|
|
|
- info.username = _get_process_user(ph, allocator) or_return
|
|
|
+ read_username: if .Username in selection {
|
|
|
+ username: string
|
|
|
+ username, err = _get_process_user(ph, allocator)
|
|
|
+ if _, ok := err.(runtime.Allocator_Error); ok {
|
|
|
+ return
|
|
|
+ } else if err != nil {
|
|
|
+ break read_username
|
|
|
+ }
|
|
|
+ info.username = username
|
|
|
info.fields += {.Username}
|
|
|
}
|
|
|
err = nil
|
|
@@ -294,15 +328,15 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
|
|
|
@(private="package")
|
|
|
_current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
|
|
info.pid = get_pid()
|
|
|
- defer if err != nil {
|
|
|
- free_process_info(info, allocator)
|
|
|
- }
|
|
|
-
|
|
|
- if selection >= {.PPid, .Priority} { // snap process
|
|
|
+ snapshot_process: if selection >= {.PPid, .Priority} {
|
|
|
entry, entry_err := _process_entry_by_pid(info.pid)
|
|
|
if entry_err != nil {
|
|
|
- err = General_Error.Not_Exist
|
|
|
- return
|
|
|
+ err = entry_err
|
|
|
+ if entry_err == General_Error.Not_Exist {
|
|
|
+ return
|
|
|
+ } else {
|
|
|
+ break snapshot_process
|
|
|
+ }
|
|
|
}
|
|
|
if .PPid in selection {
|
|
|
info.fields += {.PPid}
|
|
@@ -313,14 +347,16 @@ _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime
|
|
|
info.priority = int(entry.pcPriClassBase)
|
|
|
}
|
|
|
}
|
|
|
- if .Executable_Path in selection {
|
|
|
+ module_filename: if .Executable_Path in selection {
|
|
|
exe_filename_w: [256]u16
|
|
|
path_len := win32.GetModuleFileNameW(nil, raw_data(exe_filename_w[:]), len(exe_filename_w))
|
|
|
+ assert(path_len > 0)
|
|
|
info.executable_path = win32_utf16_to_utf8(exe_filename_w[:path_len], allocator) or_return
|
|
|
info.fields += {.Executable_Path}
|
|
|
}
|
|
|
- if selection >= {.Command_Line, .Command_Args} {
|
|
|
+ command_line: if selection >= {.Command_Line, .Command_Args} {
|
|
|
command_line_w := win32.GetCommandLineW()
|
|
|
+ assert(command_line_w != nil)
|
|
|
if .Command_Line in selection {
|
|
|
info.command_line = win32_wstring_to_utf8(command_line_w, allocator) or_return
|
|
|
info.fields += {.Command_Line}
|
|
@@ -330,14 +366,22 @@ _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime
|
|
|
info.fields += {.Command_Args}
|
|
|
}
|
|
|
}
|
|
|
- if .Environment in selection {
|
|
|
+ read_environment: if .Environment in selection {
|
|
|
env_block := win32.GetEnvironmentStringsW()
|
|
|
+ assert(env_block != nil)
|
|
|
info.environment = _parse_environment_block(env_block, allocator) or_return
|
|
|
info.fields += {.Environment}
|
|
|
}
|
|
|
- if .Username in selection {
|
|
|
+ read_username: if .Username in selection {
|
|
|
process_handle := win32.GetCurrentProcess()
|
|
|
- info.username = _get_process_user(process_handle, allocator) or_return
|
|
|
+ username: string
|
|
|
+ username, err = _get_process_user(process_handle, allocator)
|
|
|
+ if _, ok := err.(runtime.Allocator_Error); ok {
|
|
|
+ return
|
|
|
+ } else if err != nil {
|
|
|
+ break read_username
|
|
|
+ }
|
|
|
+ info.username = username
|
|
|
info.fields += {.Username}
|
|
|
}
|
|
|
if .Working_Dir in selection {
|