diff --git a/headers/private/kernel/system_profiler.h b/headers/private/kernel/system_profiler.h index 3e460f43eb..4578f5ca78 100644 --- a/headers/private/kernel/system_profiler.h +++ b/headers/private/kernel/system_profiler.h @@ -15,11 +15,16 @@ struct system_profiler_parameters; __BEGIN_DECLS +status_t start_system_profiler(size_t areaSize, uint32 flags); +void stop_system_profiler(); + status_t _user_system_profiler_start( struct system_profiler_parameters* parameters); status_t _user_system_profiler_next_buffer(size_t bytesRead, uint64* _droppedEvents); status_t _user_system_profiler_stop(); +status_t _user_system_profiler_recorded( + struct system_profiler_parameters* parameters); __END_DECLS diff --git a/headers/private/kernel/vm.h b/headers/private/kernel/vm.h index eb3dd08039..fffd9b5097 100644 --- a/headers/private/kernel/vm.h +++ b/headers/private/kernel/vm.h @@ -56,6 +56,8 @@ void forbid_page_faults(void); area_id create_area_etc(team_id team, const char *name, void **address, uint32 addressSpec, uint32 size, uint32 lock, uint32 protection, uint32 flags); +area_id transfer_area(area_id id, void** _address, uint32 addressSpec, + team_id target, bool kernel); status_t vm_unreserve_address_range(team_id team, void *address, addr_t size); status_t vm_reserve_address_range(team_id team, void **_address, diff --git a/headers/private/system/syscalls.h b/headers/private/system/syscalls.h index cbcf670ae8..558df6970e 100644 --- a/headers/private/system/syscalls.h +++ b/headers/private/system/syscalls.h @@ -436,6 +436,8 @@ extern status_t _kern_system_profiler_start( extern status_t _kern_system_profiler_next_buffer(size_t bytesRead, uint64* _droppedEvents); extern status_t _kern_system_profiler_stop(); +extern status_t _kern_system_profiler_recorded( + struct system_profiler_parameters* parameters); /* atomic_* ops (needed for CPUs that don't support them directly) */ #ifdef ATOMIC_FUNCS_ARE_SYSCALLS diff --git a/src/system/kernel/debug/system_profiler.cpp b/src/system/kernel/debug/system_profiler.cpp index 3c90245e1a..8a6c29aa6d 100644 --- a/src/system/kernel/debug/system_profiler.cpp +++ b/src/system/kernel/debug/system_profiler.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -42,6 +43,7 @@ class SystemProfiler; static spinlock sProfilerLock = B_SPINLOCK_INITIALIZER; static SystemProfiler* sProfiler = NULL; +static struct system_profiler_parameters* sRecordedParameters = NULL; class SystemProfiler : public Referenceable, private NotificationListener, @@ -1135,6 +1137,103 @@ SystemProfiler::_ProfilingEvent(struct timer* timer) } +// #pragma mark - private kernel API + + +status_t +start_system_profiler(size_t areaSize, uint32 flags) +{ + struct ParameterDeleter { + ParameterDeleter(area_id area) + : + fArea(area), + fDetached(false) + { + } + + ~ParameterDeleter() + { + if (!fDetached) { + delete_area(fArea); + delete sRecordedParameters; + sRecordedParameters = NULL; + } + } + + void Detach() + { + fDetached = true; + } + + private: + area_id fArea; + bool fDetached; + }; + + void* address; + area_id area = create_area("kernel profile data", &address, + B_ANY_KERNEL_ADDRESS, areaSize, B_FULL_LOCK, + B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); + if (area < 0) + return area; + + ParameterDeleter parameterDeleter(area); + + sRecordedParameters = new(std::nothrow) system_profiler_parameters; + if (sRecordedParameters == NULL) + return B_NO_MEMORY; + + sRecordedParameters->buffer_area = area; + sRecordedParameters->flags + = flags | B_SYSTEM_PROFILER_SAMPLING_EVENTS; + sRecordedParameters->locking_lookup_size = 4096; + sRecordedParameters->interval = 1000; + sRecordedParameters->stack_depth = 5; + + area_info areaInfo; + get_area_info(area, &areaInfo); + + // initialize the profiler + SystemProfiler* profiler = new(std::nothrow) SystemProfiler(B_SYSTEM_TEAM, + areaInfo, *sRecordedParameters); + if (profiler == NULL) + return B_NO_MEMORY; + + ObjectDeleter profilerDeleter(profiler); + + status_t error = profiler->Init(); + if (error != B_OK) + return error; + + // set the new profiler + InterruptsSpinLocker locker(sProfilerLock); + if (sProfiler != NULL) + return B_BUSY; + + parameterDeleter.Detach(); + profilerDeleter.Detach(); + sProfiler = profiler; + locker.Unlock(); + + return B_OK; +} + + +void +stop_system_profiler() +{ + InterruptsSpinLocker locker(sProfilerLock); + if (sProfiler == NULL) + return; + + SystemProfiler* profiler = sProfiler; + sProfiler = NULL; + locker.Unlock(); + + profiler->RemoveReference(); +} + + // #pragma mark - syscalls @@ -1245,3 +1344,33 @@ _user_system_profiler_stop() return B_OK; } + + +status_t +_user_system_profiler_recorded(struct system_profiler_parameters* userParameters) +{ + if (userParameters == NULL || !IS_USER_ADDRESS(userParameters)) + return B_BAD_ADDRESS; + if (sRecordedParameters == NULL) + return B_ERROR; + + // Transfer the area to the userland process + + void* address; + area_id newArea = transfer_area(sRecordedParameters->buffer_area, &address, + B_ANY_ADDRESS, team_get_current_team_id(), true); + if (newArea < 0) + return newArea; + + sRecordedParameters->buffer_area = newArea; + + status_t status = user_memcpy(userParameters, sRecordedParameters, + sizeof(system_profiler_parameters)); + if (status != B_OK) + delete_area(newArea); + + delete sRecordedParameters; + sRecordedParameters = NULL; + + return status; +} diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index e474307200..70e873c9e9 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -2621,7 +2621,7 @@ vm_delete_area(team_id team, area_id id, bool kernel) AddressSpaceWriteLocker locker; vm_area* area; status_t status = locker.SetFromArea(team, id, area); - if (status < B_OK) + if (status != B_OK) return status; if (!kernel && (area->protection & B_KERNEL_AREA) != 0) @@ -5817,25 +5817,23 @@ resize_area(area_id areaID, size_t newSize) /*! Transfers the specified area to a new team. The caller must be the owner of the area (not yet enforced but probably should be). - This function is currently not exported to the kernel namespace, but is - only accessible using the _kern_transfer_area() syscall. */ -static area_id +area_id transfer_area(area_id id, void** _address, uint32 addressSpec, team_id target, bool kernel) { area_info info; status_t status = get_area_info(id, &info); - if (status < B_OK) + if (status != B_OK) return status; area_id clonedArea = vm_clone_area(target, info.name, _address, addressSpec, info.protection, REGION_NO_PRIVATE_MAP, id, kernel); - if (clonedArea < B_OK) + if (clonedArea < 0) return clonedArea; status = vm_delete_area(info.team, id, kernel); - if (status < B_OK) { + if (status != B_OK) { vm_delete_area(target, clonedArea, kernel); return status; }