blob: add451cf79a54a3b39a69912783536b8c3950bd5 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "perfd/cpu/cpu_service.h"
#include <stdio.h>
#include "perfd/cpu/cpu_config.h"
#include "perfd/cpu/profiling_app.h"
#include "perfd/cpu/simpleperf_manager.h"
#include "proto/common.pb.h"
#include "utils/activity_manager.h"
#include "utils/file_reader.h"
#include "utils/log.h"
#include "utils/process_manager.h"
#include "utils/trace.h"
using grpc::ServerContext;
using grpc::Status;
using grpc::StatusCode;
using profiler::proto::CpuCoreConfigRequest;
using profiler::proto::CpuCoreConfigResponse;
using profiler::proto::CpuDataRequest;
using profiler::proto::CpuDataResponse;
using profiler::proto::CpuProfilerConfiguration;
using profiler::proto::CpuProfilerMode;
using profiler::proto::CpuProfilerType;
using profiler::proto::CpuProfilingAppStartRequest;
using profiler::proto::CpuProfilingAppStartResponse;
using profiler::proto::CpuProfilingAppStopRequest;
using profiler::proto::CpuProfilingAppStopResponse;
using profiler::proto::CpuStartRequest;
using profiler::proto::CpuStartResponse;
using profiler::proto::CpuStopRequest;
using profiler::proto::CpuStopResponse;
using profiler::proto::CpuUsageData;
using profiler::proto::GetThreadsRequest;
using profiler::proto::GetThreadsResponse;
using profiler::proto::GetTraceInfoRequest;
using profiler::proto::GetTraceInfoResponse;
using profiler::proto::GetTraceRequest;
using profiler::proto::GetTraceResponse;
using profiler::proto::ProfilingStateRequest;
using profiler::proto::ProfilingStateResponse;
using profiler::proto::TraceInfo;
using profiler::proto::TraceInitiationType;
using std::map;
using std::string;
using std::vector;
namespace profiler {
grpc::Status CpuServiceImpl::GetData(ServerContext* context,
const CpuDataRequest* request,
CpuDataResponse* response) {
Trace trace("CPU:GetData");
const vector<CpuUsageData>& data =
cache_.Retrieve(request->session().pid(), request->start_timestamp(),
request->end_timestamp());
for (const auto& datum : data) {
*(response->add_data()) = datum;
}
return Status::OK;
}
grpc::Status CpuServiceImpl::GetThreads(ServerContext* context,
const GetThreadsRequest* request,
GetThreadsResponse* response) {
Trace trace("CPU:GetThreads");
CpuCache::ThreadSampleResponse threads_response =
cache_.GetThreads(request->session().pid(), request->start_timestamp(),
request->end_timestamp());
// Samples containing all the activities that should be added to the response.
const vector<ThreadsSample>& samples = threads_response.activity_samples;
// Snapshot that should be included in the response.
auto snapshot = threads_response.snapshot;
if (snapshot.threads().empty()) {
// If there are no threads in the |snapshot|, we use the snapshot of the
// first sample from |samples|, in case it's not empty
if (!samples.empty()) {
*(response->mutable_initial_snapshot()) = samples.front().snapshot;
}
} else {
*(response->mutable_initial_snapshot()) = snapshot;
}
// Threads that should be added to the response, ordered by thread id.
// The activities detected by the sampled should be grouped by thread.
map<int32_t, GetThreadsResponse::Thread> threads;
for (const auto& sample : samples) {
for (const auto& activity : sample.activities) {
auto tid = activity.tid;
// Add the thread to the map if it's not there yet.
if (threads.find(tid) == threads.end()) {
GetThreadsResponse::Thread thread;
thread.set_tid(tid);
thread.set_name(activity.name);
threads[tid] = thread;
}
auto* thread_activity = threads[tid].add_activities();
thread_activity->set_timestamp(activity.timestamp);
thread_activity->set_new_state(activity.state);
}
}
// Add all the threads to the response.
for (const auto& thread : threads) {
*(response->add_threads()) = thread.second;
}
return Status::OK;
}
grpc::Status CpuServiceImpl::GetTraceInfo(ServerContext* context,
const GetTraceInfoRequest* request,
GetTraceInfoResponse* response) {
Trace trace("CPU:GetTraceInfo");
response->set_response_timestamp(clock_->GetCurrentTime());
const vector<ProfilingApp>& data =
cache_.GetCaptures(request->session().pid(), request->from_timestamp(),
request->to_timestamp());
for (const auto& datum : data) {
// Do not return in-progress captures.
if (datum.end_timestamp == -1) continue;
TraceInfo* info = response->add_trace_info();
info->set_profiler_type(datum.configuration.profiler_type());
info->set_profiler_mode(datum.configuration.profiler_mode());
info->set_initiation_type(datum.initiation_type);
info->set_from_timestamp(datum.start_timestamp);
info->set_to_timestamp(datum.end_timestamp);
info->set_trace_id(datum.trace_id);
info->set_trace_file_path(datum.trace_path);
}
return Status::OK;
}
grpc::Status CpuServiceImpl::GetTrace(ServerContext* context,
const GetTraceRequest* request,
GetTraceResponse* response) {
string content;
bool found = cache_.RetrieveTraceContent(request->session().pid(),
request->trace_id(), &content);
if (found) {
response->set_status(GetTraceResponse::SUCCESS);
response->set_data(content);
response->set_profiler_type(CpuProfilerType::ART);
response->set_profiler_mode(CpuProfilerMode::INSTRUMENTED);
} else {
response->set_status(GetTraceResponse::FAILURE);
}
return Status::OK;
}
grpc::Status CpuServiceImpl::StartMonitoringApp(ServerContext* context,
const CpuStartRequest* request,
CpuStartResponse* response) {
int32_t pid = request->session().pid();
if (!cache_.AllocateAppCache(pid)) {
return Status(StatusCode::RESOURCE_EXHAUSTED,
"Cannot allocate a cache for CPU data");
}
auto status = usage_sampler_.AddProcess(pid);
if (status == CpuStartResponse::SUCCESS) {
status = thread_monitor_.AddProcess(pid);
}
response->set_status(status);
return Status::OK;
}
grpc::Status CpuServiceImpl::StopMonitoringApp(ServerContext* context,
const CpuStopRequest* request,
CpuStopResponse* response) {
int32_t pid = request->session().pid();
auto status = usage_sampler_.RemoveProcess(pid);
if (status == CpuStopResponse::SUCCESS) {
status = thread_monitor_.RemoveProcess(pid);
}
response->set_status(status);
DoStopProfilingApp(pid, nullptr);
// cache_.DeallocateAppCache(pid) must happen last because prior actions such
// as DoStopProfilingApp() depends on data from the cache to act.
cache_.DeallocateAppCache(pid);
return Status::OK;
}
grpc::Status CpuServiceImpl::StartProfilingApp(
ServerContext* context, const CpuProfilingAppStartRequest* request,
CpuProfilingAppStartResponse* response) {
Trace trace("CPU:StartProfilingApp");
int32_t pid = request->session().pid();
ProcessManager process_manager;
string app_pkg_name = process_manager.GetCmdlineForPid(pid);
// GetCmdlineForPid will return an empty string
// if it can't find an app name corresponding to the given pid.
if (app_pkg_name.empty()) {
response->set_error_message("App is not running.");
response->set_status(CpuProfilingAppStartResponse::FAILURE);
return Status::OK;
}
bool success = false;
string error;
string trace_path;
const CpuProfilerConfiguration& configuration = request->configuration();
if (configuration.profiler_type() == CpuProfilerType::SIMPLEPERF) {
success = simpleperf_manager_->StartProfiling(
app_pkg_name, request->abi_cpu_arch(),
configuration.sampling_interval_us(), &trace_path, &error);
} else if (configuration.profiler_type() == CpuProfilerType::ATRACE) {
int acquired_buffer_size_kb = 0;
success = atrace_manager_->StartProfiling(
app_pkg_name, configuration.sampling_interval_us(),
configuration.buffer_size_in_mb(), &acquired_buffer_size_kb,
&trace_path, &error);
response->set_buffer_size_acquired_kb(acquired_buffer_size_kb);
} else {
auto mode = ActivityManager::SAMPLING;
if (configuration.profiler_mode() == CpuProfilerMode::INSTRUMENTED) {
mode = ActivityManager::INSTRUMENTED;
}
success = activity_manager_->StartProfiling(
mode, app_pkg_name, configuration.sampling_interval_us(), &trace_path,
&error);
}
if (success) {
response->set_status(CpuProfilingAppStartResponse::SUCCESS);
ProfilingApp profiling_app;
profiling_app.app_pkg_name = app_pkg_name;
profiling_app.trace_path = trace_path;
profiling_app.start_timestamp = clock_->GetCurrentTime();
profiling_app.end_timestamp = -1; // -1 means not end yet (ongoing)
profiling_app.configuration = configuration;
profiling_app.initiation_type = TraceInitiationType::INITIATED_BY_UI;
cache_.AddProfilingStart(pid, profiling_app);
} else {
response->set_status(CpuProfilingAppStartResponse::FAILURE);
response->set_error_message(error);
}
return Status::OK;
}
grpc::Status CpuServiceImpl::StopProfilingApp(
ServerContext* context, const CpuProfilingAppStopRequest* request,
CpuProfilingAppStopResponse* response) {
DoStopProfilingApp(request->session().pid(), response);
return Status::OK;
}
void CpuServiceImpl::DoStopProfilingApp(int32_t pid,
CpuProfilingAppStopResponse* response) {
ProfilingApp* app = cache_.GetOngoingCapture(pid);
if (app == nullptr) {
if (response != nullptr) {
response->set_status(CpuProfilingAppStopResponse::FAILURE);
}
return;
}
CpuProfilerType profiler_type = app->configuration.profiler_type();
string error;
bool success = false;
bool need_trace = response != nullptr;
if (profiler_type == CpuProfilerType::SIMPLEPERF) {
success = simpleperf_manager_->StopProfiling(
app->app_pkg_name, need_trace, cpu_config_.simpleperf_host(), &error);
} else if (profiler_type == CpuProfilerType::ATRACE) {
success =
atrace_manager_->StopProfiling(app->app_pkg_name, need_trace, &error);
} else { // Profiler is ART
success = activity_manager_->StopProfiling(
app->app_pkg_name, need_trace, &error,
cpu_config_.art_stop_timeout_sec(), app->is_startup_profiling);
}
if (need_trace) {
if (success) {
response->set_status(CpuProfilingAppStopResponse::SUCCESS);
string trace_content;
FileReader::Read(app->trace_path, &trace_content);
response->set_trace(trace_content);
response->set_trace_id(app->trace_id);
} else {
response->set_status(CpuProfilingAppStopResponse::FAILURE);
response->set_error_message(error);
}
}
remove(app->trace_path.c_str()); // No more use of this file. Delete it.
app->trace_path.clear();
cache_.AddProfilingStop(pid);
cache_.AddStartupProfilingStop(app->app_pkg_name);
}
grpc::Status CpuServiceImpl::CheckAppProfilingState(
ServerContext* context, const ProfilingStateRequest* request,
ProfilingStateResponse* response) {
int32_t pid = request->session().pid();
ProfilingApp* app = cache_.GetOngoingCapture(pid);
// Whether the app is being profiled (there is a stored start profiling
// request corresponding to the app)
response->set_check_timestamp(clock_->GetCurrentTime());
bool is_being_profiled = app != nullptr;
response->set_being_profiled(is_being_profiled);
if (is_being_profiled) {
// App is being profiled. Include the start profiling request and its
// timestamp in the response.
response->set_start_timestamp(app->start_timestamp);
response->set_is_startup_profiling(app->is_startup_profiling);
response->set_initiation_type(app->initiation_type);
*(response->mutable_configuration()) = app->configuration;
}
return Status::OK;
}
grpc::Status CpuServiceImpl::StartStartupProfiling(
grpc::ServerContext* context,
const profiler::proto::StartupProfilingRequest* request,
profiler::proto::StartupProfilingResponse* response) {
ProfilingApp app;
app.app_pkg_name = request->app_package();
app.start_timestamp = clock_->GetCurrentTime();
app.end_timestamp = -1;
app.configuration = request->configuration();
app.initiation_type = TraceInitiationType::INITIATED_BY_STARTUP;
app.is_startup_profiling = true;
CpuProfilerType profiler_type = app.configuration.profiler_type();
string error;
bool success = false;
// TODO: Art should be handled by Debug.startMethodTracing and
// Debug.stopMethodTracing APIs instrumentation instead. Once our codebase
// supports instrumenting them, this code should be removed.
if (profiler_type == CpuProfilerType::ART) {
auto mode = ActivityManager::SAMPLING;
if (app.configuration.profiler_mode() == CpuProfilerMode::INSTRUMENTED) {
mode = ActivityManager::INSTRUMENTED;
}
success = activity_manager_->StartProfiling(
mode, app.app_pkg_name, app.configuration.sampling_interval_us(),
&app.trace_path, &error, true);
response->set_file_path(app.trace_path);
} else if (profiler_type == CpuProfilerType::SIMPLEPERF) {
success = simpleperf_manager_->StartProfiling(
app.app_pkg_name, request->abi_cpu_arch(),
app.configuration.sampling_interval_us(), &app.trace_path, &error,
true);
} else if (profiler_type == CpuProfilerType::ATRACE) {
int acquired_buffer_size_kb = 0;
success = atrace_manager_->StartProfiling(
app.app_pkg_name, app.configuration.sampling_interval_us(),
app.configuration.buffer_size_in_mb(), &acquired_buffer_size_kb,
&app.trace_path, &error);
response->set_buffer_size_acquired_kb(acquired_buffer_size_kb);
}
if (success) {
response->set_status(proto::StartupProfilingResponse::SUCCESS);
} else {
response->set_status(proto::StartupProfilingResponse::FAILURE);
response->set_error_message(error);
}
cache_.AddStartupProfilingStart(app.app_pkg_name, app);
return Status::OK;
}
int64_t CpuServiceImpl::GetEarliestDataTime(int32_t pid) {
string app_pkg_name = ProcessManager::GetCmdlineForPid(pid);
ProfilingApp* app = cache_.GetOngoingStartupProfiling(app_pkg_name);
return app != nullptr ? app->start_timestamp : LLONG_MAX;
}
Status CpuServiceImpl::GetCpuCoreConfig(ServerContext* context,
const CpuCoreConfigRequest* request,
CpuCoreConfigResponse* response) {
return CpuConfig::GetCpuCoreConfig(response);
}
} // namespace profiler