diff --git a/dlls/ntdll/process.c b/dlls/ntdll/process.c index 952225688cb..8d660300aaa 100644 --- a/dlls/ntdll/process.c +++ b/dlls/ntdll/process.c @@ -294,15 +294,29 @@ NTSTATUS WINAPI NtQueryInformationProcess( { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; - else if (!ProcessHandle) - ret = STATUS_INVALID_HANDLE; else { memset(&pvmi, 0 , sizeof(VM_COUNTERS)); if (ProcessHandle == GetCurrentProcess()) fill_VM_COUNTERS(&pvmi); else - FIXME("Need wineserver call to get VM counters for another process\n"); + { + SERVER_START_REQ(get_process_vm_counters) + { + req->handle = wine_server_obj_handle( ProcessHandle ); + if (!(ret = wine_server_call( req ))) + { + pvmi.PeakVirtualSize = reply->peak_virtual_size; + pvmi.VirtualSize = reply->virtual_size; + pvmi.PeakWorkingSetSize = reply->peak_working_set_size; + pvmi.WorkingSetSize = reply->working_set_size; + pvmi.PagefileUsage = reply->pagefile_usage; + pvmi.PeakPagefileUsage = reply->peak_pagefile_usage; + } + } + SERVER_END_REQ; + if (ret) break; + } len = ProcessInformationLength; if (len != FIELD_OFFSET(VM_COUNTERS,PrivatePageCount)) len = sizeof(VM_COUNTERS); diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index 35e25afd0eb..f8759fe9bd1 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -1052,6 +1052,7 @@ static void test_query_process_vm(void) ULONG ReturnLength; VM_COUNTERS pvi; ULONG old_size = FIELD_OFFSET(VM_COUNTERS,PrivatePageCount); + HANDLE process; status = pNtQueryInformationProcess(NULL, ProcessVmCounters, NULL, sizeof(pvi), NULL); ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE, @@ -1079,6 +1080,27 @@ static void test_query_process_vm(void) /* Check if we have some return values */ trace("WorkingSetSize : %ld\n", pvi.WorkingSetSize); ok( pvi.WorkingSetSize > 0, "Expected a WorkingSetSize > 0\n"); + + process = OpenProcess(PROCESS_VM_READ, FALSE, GetCurrentProcessId()); + status = pNtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL); + ok( status == STATUS_ACCESS_DENIED, "Expected STATUS_ACCESS_DENIED, got %08x\n", status); + CloseHandle(process); + + process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, GetCurrentProcessId()); + status = pNtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL); + ok( status == STATUS_SUCCESS || broken(!process) /* XP */, "Expected STATUS_SUCCESS, got %08x\n", status); + CloseHandle(process); + + memset(&pvi, 0, sizeof(pvi)); + process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); + status = pNtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + + /* Check if we have some return values */ + trace("WorkingSetSize : %ld\n", pvi.WorkingSetSize); + ok( pvi.WorkingSetSize > 0, "Expected a WorkingSetSize > 0\n"); + + CloseHandle(process); } static void test_query_process_io(void) diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index af792c6f87d..cee0f57c639 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -885,6 +885,24 @@ struct get_process_info_reply +struct get_process_vm_counters_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct get_process_vm_counters_reply +{ + struct reply_header __header; + mem_size_t peak_virtual_size; + mem_size_t virtual_size; + mem_size_t peak_working_set_size; + mem_size_t working_set_size; + mem_size_t pagefile_usage; + mem_size_t peak_pagefile_usage; +}; + + + struct set_process_info_request { struct request_header __header; @@ -5541,6 +5559,7 @@ enum request REQ_terminate_process, REQ_terminate_thread, REQ_get_process_info, + REQ_get_process_vm_counters, REQ_set_process_info, REQ_get_thread_info, REQ_get_thread_times, @@ -5831,6 +5850,7 @@ union generic_request struct terminate_process_request terminate_process_request; struct terminate_thread_request terminate_thread_request; struct get_process_info_request get_process_info_request; + struct get_process_vm_counters_request get_process_vm_counters_request; struct set_process_info_request set_process_info_request; struct get_thread_info_request get_thread_info_request; struct get_thread_times_request get_thread_times_request; @@ -6119,6 +6139,7 @@ union generic_reply struct terminate_process_reply terminate_process_reply; struct terminate_thread_reply terminate_thread_reply; struct get_process_info_reply get_process_info_reply; + struct get_process_vm_counters_reply get_process_vm_counters_reply; struct set_process_info_reply set_process_info_reply; struct get_thread_info_reply get_thread_info_reply; struct get_thread_times_reply get_thread_times_reply; @@ -6395,6 +6416,6 @@ union generic_reply struct terminate_job_reply terminate_job_reply; }; -#define SERVER_PROTOCOL_VERSION 530 +#define SERVER_PROTOCOL_VERSION 531 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/process.c b/server/process.c index e9e2f21de2b..3e40c9eeafd 100644 --- a/server/process.c +++ b/server/process.c @@ -1371,6 +1371,44 @@ DECL_HANDLER(get_process_info) } } +/* retrieve information about a process memory usage */ +DECL_HANDLER(get_process_vm_counters) +{ + struct process *process = get_process_from_handle( req->handle, PROCESS_QUERY_LIMITED_INFORMATION ); + + if (!process) return; +#ifdef linux + if (process->unix_pid != -1) + { + FILE *f; + char proc_path[32], line[256]; + unsigned long value; + + sprintf( proc_path, "/proc/%u/status", process->unix_pid ); + if ((f = fopen( proc_path, "r" ))) + { + while (fgets( line, sizeof(line), f )) + { + if (sscanf( line, "VmPeak: %lu", &value )) + reply->peak_virtual_size = (mem_size_t)value * 1024; + else if (sscanf( line, "VmSize: %lu", &value )) + reply->virtual_size = (mem_size_t)value * 1024; + else if (sscanf( line, "VmHWM: %lu", &value )) + reply->peak_working_set_size = (mem_size_t)value * 1024; + else if (sscanf( line, "VmRSS: %lu", &value )) + reply->working_set_size = (mem_size_t)value * 1024; + else if (sscanf( line, "VmSwap: %lu", &value )) + reply->peak_pagefile_usage = reply->pagefile_usage = (mem_size_t)value * 1024; + } + fclose( f ); + } + else set_error( STATUS_ACCESS_DENIED ); + } + else set_error( STATUS_ACCESS_DENIED ); +#endif + release_object( process ); +} + static void set_process_affinity( struct process *process, affinity_t affinity ) { struct thread *thread; diff --git a/server/protocol.def b/server/protocol.def index e9de52214bb..0a9d258fa63 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -845,6 +845,19 @@ struct rawinput_device @END +/* Retrieve information about a process memory usage */ +@REQ(get_process_vm_counters) + obj_handle_t handle; /* process handle */ +@REPLY + mem_size_t peak_virtual_size; /* peak virtual memory in bytes */ + mem_size_t virtual_size; /* virtual memory in bytes */ + mem_size_t peak_working_set_size; /* peak real memory in bytes */ + mem_size_t working_set_size; /* real memory in bytes */ + mem_size_t pagefile_usage; /* swapped-out memory in bytes */ + mem_size_t peak_pagefile_usage; /* peak swapped-out memory in bytes */ +@END + + /* Set a process information */ @REQ(set_process_info) obj_handle_t handle; /* process handle */ diff --git a/server/request.h b/server/request.h index d9e27b8c5e9..8b9d9b61f53 100644 --- a/server/request.h +++ b/server/request.h @@ -121,6 +121,7 @@ DECL_HANDLER(init_thread); DECL_HANDLER(terminate_process); DECL_HANDLER(terminate_thread); DECL_HANDLER(get_process_info); +DECL_HANDLER(get_process_vm_counters); DECL_HANDLER(set_process_info); DECL_HANDLER(get_thread_info); DECL_HANDLER(get_thread_times); @@ -410,6 +411,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_terminate_process, (req_handler)req_terminate_thread, (req_handler)req_get_process_info, + (req_handler)req_get_process_vm_counters, (req_handler)req_set_process_info, (req_handler)req_get_thread_info, (req_handler)req_get_thread_times, @@ -796,6 +798,15 @@ C_ASSERT( FIELD_OFFSET(struct get_process_info_reply, cpu) == 56 ); C_ASSERT( FIELD_OFFSET(struct get_process_info_reply, debugger_present) == 60 ); C_ASSERT( FIELD_OFFSET(struct get_process_info_reply, debug_children) == 62 ); C_ASSERT( sizeof(struct get_process_info_reply) == 64 ); +C_ASSERT( FIELD_OFFSET(struct get_process_vm_counters_request, handle) == 12 ); +C_ASSERT( sizeof(struct get_process_vm_counters_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_process_vm_counters_reply, peak_virtual_size) == 8 ); +C_ASSERT( FIELD_OFFSET(struct get_process_vm_counters_reply, virtual_size) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_process_vm_counters_reply, peak_working_set_size) == 24 ); +C_ASSERT( FIELD_OFFSET(struct get_process_vm_counters_reply, working_set_size) == 32 ); +C_ASSERT( FIELD_OFFSET(struct get_process_vm_counters_reply, pagefile_usage) == 40 ); +C_ASSERT( FIELD_OFFSET(struct get_process_vm_counters_reply, peak_pagefile_usage) == 48 ); +C_ASSERT( sizeof(struct get_process_vm_counters_reply) == 56 ); C_ASSERT( FIELD_OFFSET(struct set_process_info_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct set_process_info_request, mask) == 16 ); C_ASSERT( FIELD_OFFSET(struct set_process_info_request, priority) == 20 ); diff --git a/server/trace.c b/server/trace.c index d830e4f9c18..7b33043cdf7 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1346,6 +1346,21 @@ static void dump_get_process_info_reply( const struct get_process_info_reply *re fprintf( stderr, ", debug_children=%d", req->debug_children ); } +static void dump_get_process_vm_counters_request( const struct get_process_vm_counters_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + +static void dump_get_process_vm_counters_reply( const struct get_process_vm_counters_reply *req ) +{ + dump_uint64( " peak_virtual_size=", &req->peak_virtual_size ); + dump_uint64( ", virtual_size=", &req->virtual_size ); + dump_uint64( ", peak_working_set_size=", &req->peak_working_set_size ); + dump_uint64( ", working_set_size=", &req->working_set_size ); + dump_uint64( ", pagefile_usage=", &req->pagefile_usage ); + dump_uint64( ", peak_pagefile_usage=", &req->peak_pagefile_usage ); +} + static void dump_set_process_info_request( const struct set_process_info_request *req ) { fprintf( stderr, " handle=%04x", req->handle ); @@ -4457,6 +4472,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_terminate_process_request, (dump_func)dump_terminate_thread_request, (dump_func)dump_get_process_info_request, + (dump_func)dump_get_process_vm_counters_request, (dump_func)dump_set_process_info_request, (dump_func)dump_get_thread_info_request, (dump_func)dump_get_thread_times_request, @@ -4743,6 +4759,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_terminate_process_reply, (dump_func)dump_terminate_thread_reply, (dump_func)dump_get_process_info_reply, + (dump_func)dump_get_process_vm_counters_reply, NULL, (dump_func)dump_get_thread_info_reply, (dump_func)dump_get_thread_times_reply, @@ -5029,6 +5046,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "terminate_process", "terminate_thread", "get_process_info", + "get_process_vm_counters", "set_process_info", "get_thread_info", "get_thread_times",