From 64b0d5aac0c5214db01f938550e8b126891d5a08 Mon Sep 17 00:00:00 2001 From: Simon Schubert <2@0x2c.org> Date: Fri, 1 Sep 2017 20:34:09 +0200 Subject: [PATCH] cortex_m: add profiling function Use DWT_PCSR if present (reads nonzero); otherwise do halt-and-sample pc. Signed-off-by: Simon Schubert <2@0x2c.org> Change-Id: Id2dc4665e5008cc497a2e6e6493522d038d5af42 Reviewed-on: http://openocd.zylin.com/4211 Tested-by: jenkins Reviewed-by: Karl Palsson Reviewed-by: Christopher Head Reviewed-by: Paul Fertser --- src/target/cortex_m.c | 89 +++++++++++++++++++++++++++++++++++++++++++ src/target/cortex_m.h | 1 + 2 files changed, 90 insertions(+) diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c index 2f8c2a2c8d..988323b042 100644 --- a/src/target/cortex_m.c +++ b/src/target/cortex_m.c @@ -1707,6 +1707,93 @@ void cortex_m_deinit_target(struct target *target) free(cortex_m); } +static int cortex_m_profiling(struct target *target, uint32_t *samples, + uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds) +{ + struct timeval timeout, now; + struct armv7m_common *armv7m = target_to_armv7m(target); + uint32_t reg_value; + bool use_pcsr = false; + int retval = ERROR_OK; + struct reg *reg; + + gettimeofday(&timeout, NULL); + timeval_add_time(&timeout, seconds, 0); + + retval = target_read_u32(target, DWT_PCSR, ®_value); + if (retval != ERROR_OK) { + LOG_ERROR("Error while reading PCSR"); + return retval; + } + + if (reg_value != 0) { + use_pcsr = true; + LOG_INFO("Starting Cortex-M profiling. Sampling DWT_PCSR as fast as we can..."); + } else { + LOG_INFO("Starting profiling. Halting and resuming the" + " target as often as we can..."); + reg = register_get_by_name(target->reg_cache, "pc", 1); + } + + /* Make sure the target is running */ + target_poll(target); + if (target->state == TARGET_HALTED) + retval = target_resume(target, 1, 0, 0, 0); + + if (retval != ERROR_OK) { + LOG_ERROR("Error while resuming target"); + return retval; + } + + uint32_t sample_count = 0; + + for (;;) { + if (use_pcsr) { + uint32_t read_count = max_num_samples - sample_count; + if (read_count > 1024) + read_count = 1024; + retval = mem_ap_read_buf_noincr(armv7m->debug_ap, + (void *)&samples[sample_count], + 4, read_count, DWT_PCSR); + sample_count += read_count; + } else { + target_poll(target); + if (target->state == TARGET_HALTED) { + reg_value = buf_get_u32(reg->value, 0, 32); + /* current pc, addr = 0, do not handle breakpoints, not debugging */ + retval = target_resume(target, 1, 0, 0, 0); + samples[sample_count++] = reg_value; + target_poll(target); + alive_sleep(10); /* sleep 10ms, i.e. <100 samples/second. */ + } else if (target->state == TARGET_RUNNING) { + /* We want to quickly sample the PC. */ + retval = target_halt(target); + } else { + LOG_INFO("Target not halted or running"); + retval = ERROR_OK; + break; + } + } + + if (retval != ERROR_OK) { + LOG_ERROR("Error while reading %s", use_pcsr ? "PCSR" : "target pc"); + return retval; + } + + + gettimeofday(&now, NULL); + if (sample_count >= max_num_samples || + (now.tv_sec >= timeout.tv_sec && now.tv_usec >= timeout.tv_usec)) { + LOG_INFO("Profiling completed. %" PRIu32 " samples.", sample_count); + break; + } + } + + *num_samples = sample_count; + return retval; +} + + /* REVISIT cache valid/dirty bits are unmaintained. We could set "valid" * on r/w if the core is not running, and clear on resume or reset ... or * at least, in a post_restore_context() method. @@ -2451,4 +2538,6 @@ struct target_type cortexm_target = { .init_target = cortex_m_init_target, .examine = cortex_m_examine, .deinit_target = cortex_m_deinit_target, + + .profiling = cortex_m_profiling, }; diff --git a/src/target/cortex_m.h b/src/target/cortex_m.h index 3d9714b900..dbf9ac8a7f 100644 --- a/src/target/cortex_m.h +++ b/src/target/cortex_m.h @@ -48,6 +48,7 @@ #define DWT_CTRL 0xE0001000 #define DWT_CYCCNT 0xE0001004 +#define DWT_PCSR 0xE000101C #define DWT_COMP0 0xE0001020 #define DWT_MASK0 0xE0001024 #define DWT_FUNCTION0 0xE0001028 -- 2.30.2