static void* _profile_stream_thread(void* arg) { FOUNDATION_UNUSED(arg); thread_yield(); while (!thread_try_wait(4)) { profile_log(STRING_CONST("Thread message")); profile_begin_block(STRING_CONST("Thread block")); { profile_update_block(); profile_begin_block(STRING_CONST("Thread subblock")); { profile_log(STRING_CONST("Sub message")); profile_trylock(STRING_CONST("Trylock")); profile_lock(STRING_CONST("Trylock")); profile_wait(STRING_CONST("Wait")); profile_signal(STRING_CONST("Signal")); thread_sleep(2); profile_unlock(STRING_CONST("Trylock")); profile_log(STRING_CONST("End sub")); } profile_end_block(); profile_begin_block(STRING_CONST("Thread second subblock")); { profile_update_block(); profile_begin_block(STRING_CONST("Thread subblock")); { } profile_end_block(); } profile_end_block(); profile_trylock(STRING_CONST("Trylock")); thread_sleep(1); profile_lock(STRING_CONST("Trylock")); thread_sleep(4); profile_unlock(STRING_CONST("Trylock")); } profile_end_block(); atomic_add64(&_profile_generated_blocks, 14); } return 0; }
static void* _profile_io( object_t thread, void* arg ) { unsigned int system_info_counter = 0; profile_block_t system_info; FOUNDATION_UNUSED( arg ); memset( &system_info, 0, sizeof( profile_block_t ) ); system_info.data.id = PROFILE_ID_SYSTEMINFO; system_info.data.start = time_ticks_per_second(); string_copy( system_info.data.name, "sysinfo", 7 ); while( !thread_should_terminate( thread ) ) { thread_sleep( _profile_wait ); if( !atomic_load32( &_profile_root ) ) continue; profile_begin_block( "profile_io" ); if( atomic_load32( &_profile_root ) ) { profile_begin_block( "process" ); //This is thread safe in the sense that only completely closed and ended //blocks will be put as children to root block, so no additional blocks //will ever be added to child subtrees while we process it here _profile_process_root_block(); profile_end_block(); } if( system_info_counter++ > 10 ) { if( _profile_write ) _profile_write( &system_info, sizeof( profile_block_t ) ); system_info_counter = 0; } profile_end_block(); } if( atomic_load32( &_profile_root ) ) _profile_process_root_block(); if( _profile_write ) { profile_block_t terminate; memset( &terminate, 0, sizeof( profile_block_t ) ); terminate.data.id = PROFILE_ID_ENDOFSTREAM; _profile_write( &terminate, sizeof( profile_block_t ) ); } return 0; }
static void* _profile_stream_thread( object_t thread, void* arg ) { FOUNDATION_UNUSED( arg ); thread_yield(); while( !thread_should_terminate( thread ) ) { profile_log( "Thread message" ); profile_begin_block( "Thread block" ); { profile_update_block(); profile_begin_block( "Thread subblock" ); { profile_log( "Sub message" ); profile_trylock( "Trylock" ); profile_lock( "Trylock" ); profile_wait( "Wait" ); profile_signal( "Signal" ); thread_sleep( 2 ); profile_unlock( "Trylock" ); profile_log( "End sub" ); } profile_end_block(); profile_trylock( "Trylock" ); thread_sleep( 1 ); profile_lock( "Trylock" ); thread_sleep( 4 ); profile_unlock( "Trylock" ); } profile_end_block(); thread_sleep( 4 ); atomic_add64( &_profile_generated_blocks, 12 ); } return 0; }
void profile_end_block( void ) { uint32_t block_index = get_thread_profile_block(); profile_block_t* block; if( !_profile_enable || !block_index ) return; block = GET_BLOCK( block_index ); block->data.end = time_current() - _profile_ground_time; if( block->previous ) { unsigned int processor; profile_block_t* current = block; profile_block_t* previous = GET_BLOCK( block->previous ); profile_block_t* parent; unsigned int current_index = block_index; unsigned int parent_index; while( previous->child != current_index ) { current_index = current->previous; //Walk sibling list backwards current = GET_BLOCK( current_index ); previous = GET_BLOCK( current->previous ); #if PROFILE_ENABLE_SANITY_CHECKS FOUNDATION_ASSERT( current_index != 0 ); FOUNDATION_ASSERT( current->previous != 0 ); #endif } parent_index = current->previous; //Previous now points to parent parent = GET_BLOCK( parent_index ); #if PROFILE_ENABLE_SANITY_CHECKS FOUNDATION_ASSERT( parent_index != block_index ); #endif set_thread_profile_block( parent_index ); processor = thread_hardware(); if( parent->data.processor != processor ) { const char* message = parent->data.name; //Thread migrated, split into new block profile_end_block(); profile_begin_block( message ); } } else { _profile_put_root_block( block_index ); set_thread_profile_block( 0 ); } }
static void* _profile_fail_thread(void* arg) { FOUNDATION_UNUSED(arg); thread_sleep(10); while (!thread_try_wait(1)) { profile_log(STRING_CONST("Thread message")); profile_begin_block(STRING_CONST("Thread block")); { profile_update_block(); profile_begin_block(STRING_CONST("Thread subblock")); { profile_log(STRING_CONST("Sub message")); profile_trylock(STRING_CONST("Trylock")); profile_lock(STRING_CONST("Trylock")); profile_wait(STRING_CONST("Wait")); profile_signal(STRING_CONST("Signal")); profile_unlock(STRING_CONST("Trylock")); profile_log(STRING_CONST("End sub")); thread_yield(); } profile_end_block(); } profile_end_block(); } return 0; }
void profile_update_block( void ) { char* message; unsigned int processor; uint32_t block_index = get_thread_profile_block(); profile_block_t* block; if( !_profile_enable || !block_index ) return; block = GET_BLOCK( block_index ); message = block->data.name; processor = thread_hardware(); if( block->data.processor == processor ) return; //Thread migrated to another core, split into new block profile_end_block(); profile_begin_block( message ); }
void lua_execute_pending(lua_t* env) { profile_begin_block(STRING_CONST("lua exec")); unsigned int head = env->queue_head; while (env->queue[head].cmd != LUACMD_WAIT) { //Execute switch (env->queue[head].cmd) { case LUACMD_LOAD: lua_do_eval_stream(env, env->queue[head].data.ptr); break; case LUACMD_EVAL: lua_do_eval_string(env, env->queue[head].data.name, env->queue[head].size); break; case LUACMD_CALL: lua_do_call_custom(env, env->queue[head].data.name, env->queue[head].size, &env->queue[head].arg); break; case LUACMD_BIND: case LUACMD_BIND_INT: case LUACMD_BIND_VAL: lua_do_bind(env, env->queue[head].data.name, env->queue[head].size, env->queue[head].cmd, env->queue[head].arg.value[0]); break; default: break; } //Mark as executed env->queue[head].cmd = LUACMD_WAIT; if (++head == BUILD_LUA_CALL_QUEUE_SIZE) head = 0; } env->queue_head = head; profile_end_block(); }
void system_process_events( void ) { #if FOUNDATION_PLATFORM_ANDROID profile_begin_block( "system events" ); int ident = 0; int events = 0; int nummsg = 0; struct android_poll_source* source = 0; struct android_app* app = android_app(); while( ( ident = ALooper_pollAll( 0, 0, &events, (void**)&source ) ) >= 0 ) { // Process this event. if( source ) source->process( app, source ); ++nummsg; } profile_end_block(); #endif }
DECLARE_TEST(profile, stream) { thread_t thread[32]; int ith; uint64_t frame; string_t filename; error(); //Clear error filename = path_allocate_concat(STRING_ARGS(environment_temporary_directory()), STRING_CONST("test.profile")); //log_infof(HASH_TEST, STRING_CONST("Output to profile file: %.*s"), STRING_FORMAT(filename)); fs_make_directory(STRING_ARGS(environment_temporary_directory())); _profile_stream = fs_open_file(STRING_ARGS(filename), STREAM_OUT | STREAM_BINARY); string_deallocate(filename.str); profile_initialize(STRING_CONST("test_profile"), _test_profile_buffer, TEST_PROFILE_BUFFER_SIZE); profile_set_output(_profile_file_writer); profile_set_output_wait(10); profile_enable(true); for (ith = 0; ith < 32; ++ith) thread_initialize(&thread[ith], _profile_stream_thread, 0, STRING_CONST("profile_thread"), THREAD_PRIORITY_NORMAL, 0); for (ith = 0; ith < 32; ++ith) thread_start(&thread[ith]); test_wait_for_threads_startup(thread, 32); for (frame = 0; frame < 1000; ++frame) { thread_sleep(16); profile_log( STRING_CONST("This is a really long profile log line that should break into multiple profile blocks automatically without causing any issues whatsoever if everything works as expected which it should or the code needs to be fixed")); profile_end_frame(frame++); if ((frame % 30) == 0) { profile_enable(false); thread_sleep(10); profile_enable(true); } } for (ith = 0; ith < 32; ++ith) thread_signal(&thread[ith]); test_wait_for_threads_finish(thread, 32); for (ith = 0; ith < 32; ++ith) thread_finalize(&thread[ith]); profile_end_frame(frame++); profile_set_output_wait(10000); thread_sleep(1000); profile_begin_block(STRING_CONST("Should be cleaned up")); profile_end_block(); profile_enable(false); profile_finalize(); error(); stream_deallocate(_profile_stream); //TODO: Validate that output is sane log_debugf(HASH_TEST, STRING_CONST("Generated %" PRId64 " blocks"), atomic_load64(&_profile_generated_blocks)); return 0; }