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 <karlp@tweak.net.au>
Reviewed-by: Christopher Head <chead@zaber.com>
Reviewed-by: Paul Fertser <fercerpav@gmail.com>
This commit is contained in:
Simon Schubert 2017-09-01 20:34:09 +02:00 committed by Paul Fertser
parent 2b44b52478
commit 64b0d5aac0
2 changed files with 90 additions and 0 deletions

View File

@ -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, &reg_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,
};

View File

@ -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