1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-05 12:10:55 +00:00

First version of Profiler

It is now possible to profile GDScript as well as some parts of Godot
internals.
This commit is contained in:
Juan Linietsky
2016-05-21 21:18:16 -03:00
parent c195c0df6b
commit a75f896338
35 changed files with 2245 additions and 180 deletions

View File

@@ -125,6 +125,10 @@ static ObjectID safe_get_instance_id(const Variant& p_v) {
void ScriptDebuggerRemote::debug(ScriptLanguage *p_script,bool p_can_continue) {
//this function is called when there is a debugger break (bug on script)
//or when execution is paused from editor
if (!tcp_client->is_connected()) {
ERR_EXPLAIN("Script Debugger failed to connect, but being used anyway.");
ERR_FAIL();
@@ -135,6 +139,8 @@ void ScriptDebuggerRemote::debug(ScriptLanguage *p_script,bool p_can_continue) {
packet_peer_stream->put_var(p_can_continue);
packet_peer_stream->put_var(p_script->debug_get_error());
skip_profile_frame=true; // to avoid super long frame time for the frame
Input::MouseMode mouse_mode=Input::get_singleton()->get_mouse_mode();
if (mouse_mode!=Input::MOUSE_MODE_VISIBLE)
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
@@ -372,7 +378,7 @@ void ScriptDebuggerRemote::line_poll() {
//the purpose of this is just processing events every now and then when the script might get too busy
//otherwise bugs like infinite loops cant be catched
if (poll_every%512==0)
if (poll_every%2048==0)
_poll_events();
poll_every++;
@@ -535,6 +541,9 @@ bool ScriptDebuggerRemote::_parse_live_edit(const Array& cmd) {
void ScriptDebuggerRemote::_poll_events() {
//this si called from ::idle_poll, happens only when running the game,
//does not get called while on debug break
while(packet_peer_stream->get_available_packet_count()>0) {
_get_output();
@@ -566,6 +575,31 @@ void ScriptDebuggerRemote::_poll_events() {
} else if (command=="request_video_mem") {
_send_video_memory();
} else if (command=="start_profiling") {
for(int i=0;i<ScriptServer::get_language_count();i++) {
ScriptServer::get_language(i)->profiling_start();
}
max_frame_functions=cmd[1];
profiler_function_signature_map.clear();
profiling=true;
frame_time=0;
idle_time=0;
fixed_time=0;
fixed_frame_time=0;
print_line("PROFILING ALRIGHT!");
} else if (command=="stop_profiling") {
for(int i=0;i<ScriptServer::get_language_count();i++) {
ScriptServer::get_language(i)->profiling_stop();
}
profiling=false;
_send_profiling_data(false);
print_line("PROFILING END!");
} else if (command=="breakpoint") {
bool set = cmd[3];
@@ -582,8 +616,113 @@ void ScriptDebuggerRemote::_poll_events() {
}
void ScriptDebuggerRemote::_send_profiling_data(bool p_for_frame) {
int ofs=0;
for(int i=0;i<ScriptServer::get_language_count();i++) {
if (p_for_frame)
ofs+=ScriptServer::get_language(i)->profiling_get_frame_data(&profile_info[ofs],profile_info.size()-ofs);
else
ofs+=ScriptServer::get_language(i)->profiling_get_accumulated_data(&profile_info[ofs],profile_info.size()-ofs);
}
for(int i=0;i<ofs;i++) {
profile_info_ptrs[i]=&profile_info[i];
}
SortArray<ScriptLanguage::ProfilingInfo*,ProfileInfoSort> sa;
sa.sort(profile_info_ptrs.ptr(),ofs);
int to_send=MIN(ofs,max_frame_functions);
//check signatures first
uint64_t total_script_time=0;
for(int i=0;i<to_send;i++) {
if (!profiler_function_signature_map.has(profile_info_ptrs[i]->signature)) {
int idx = profiler_function_signature_map.size();
packet_peer_stream->put_var("profile_sig");
packet_peer_stream->put_var(2);
packet_peer_stream->put_var(profile_info_ptrs[i]->signature);
packet_peer_stream->put_var(idx);
profiler_function_signature_map[profile_info_ptrs[i]->signature]=idx;
}
total_script_time+=profile_info_ptrs[i]->self_time;
}
//send frames then
if (p_for_frame) {
packet_peer_stream->put_var("profile_frame");
packet_peer_stream->put_var(8+profile_frame_data.size()*2+to_send*4);
} else {
packet_peer_stream->put_var("profile_total");
packet_peer_stream->put_var(8+to_send*4);
}
packet_peer_stream->put_var(OS::get_singleton()->get_frames_drawn()); //total frame time
packet_peer_stream->put_var(frame_time); //total frame time
packet_peer_stream->put_var(idle_time); //idle frame time
packet_peer_stream->put_var(fixed_time); //fixed frame time
packet_peer_stream->put_var(fixed_frame_time); //fixed frame time
packet_peer_stream->put_var(USEC_TO_SEC(total_script_time)); //total script execution time
if (p_for_frame) {
packet_peer_stream->put_var(profile_frame_data.size()); //how many profile framedatas to send
packet_peer_stream->put_var(to_send); //how many script functions to send
for (int i=0;i<profile_frame_data.size();i++) {
packet_peer_stream->put_var(profile_frame_data[i].name);
packet_peer_stream->put_var(profile_frame_data[i].data);
}
} else {
packet_peer_stream->put_var(0); //how many script functions to send
packet_peer_stream->put_var(to_send); //how many script functions to send
}
for(int i=0;i<to_send;i++) {
int sig_id=-1;
if (profiler_function_signature_map.has(profile_info_ptrs[i]->signature)) {
sig_id=profiler_function_signature_map[profile_info_ptrs[i]->signature];
}
packet_peer_stream->put_var(sig_id);
packet_peer_stream->put_var(profile_info_ptrs[i]->call_count);
packet_peer_stream->put_var(profile_info_ptrs[i]->total_time/1000000.0);
packet_peer_stream->put_var(profile_info_ptrs[i]->self_time/1000000.0);
}
if (p_for_frame) {
profile_frame_data.clear();
}
}
void ScriptDebuggerRemote::idle_poll() {
// this function is called every frame, except when there is a debugger break (::debug() in this class)
// execution stops and remains in the ::debug function
_get_output();
@@ -615,6 +754,16 @@ void ScriptDebuggerRemote::idle_poll() {
}
}
if (profiling) {
if (skip_profile_frame) {
skip_profile_frame=false;
} else {
//send profiling info normally
_send_profiling_data(true);
}
}
_poll_events();
}
@@ -687,6 +836,50 @@ void ScriptDebuggerRemote::set_live_edit_funcs(LiveEditFuncs *p_funcs) {
live_edit_funcs=p_funcs;
}
bool ScriptDebuggerRemote::is_profiling() const {
return profiling;
}
void ScriptDebuggerRemote::add_profiling_frame_data(const StringName& p_name,const Array& p_data){
int idx=-1;
for(int i=0;i<profile_frame_data.size();i++) {
if (profile_frame_data[i].name==p_name) {
idx=i;
break;
}
}
FrameData fd;
fd.name=p_name;
fd.data=p_data;
if (idx==-1) {
profile_frame_data.push_back(fd);
} else {
profile_frame_data[idx]=fd;
}
}
void ScriptDebuggerRemote::profiling_start() {
//ignores this, uses it via connnection
}
void ScriptDebuggerRemote::profiling_end() {
//ignores this, uses it via connnection
}
void ScriptDebuggerRemote::profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_fixed_time, float p_fixed_frame_time) {
frame_time=p_frame_time;
idle_time=p_idle_time;
fixed_time=p_fixed_time;
fixed_frame_time=p_fixed_frame_time;
}
ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_func=NULL;
ScriptDebuggerRemote::ScriptDebuggerRemote() {
@@ -710,11 +903,17 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() {
char_count=0;
msec_count=0;
last_msec=0;
skip_profile_frame=false;
eh.errfunc=_err_handler;
eh.userdata=this;
add_error_handler(&eh);
profile_info.resize(CLAMP(int(Globals::get_singleton()->get("debug/profiler_max_functions")),128,65535));
profile_info_ptrs.resize(profile_info.size());
profiling=false;
max_frame_functions=16;
}
ScriptDebuggerRemote::~ScriptDebuggerRemote() {
@@ -723,4 +922,5 @@ ScriptDebuggerRemote::~ScriptDebuggerRemote() {
remove_error_handler(&eh);
memdelete(mutex);
}