target: add profiling interface
Profiling could be target-specific. Add .profiling interface to target_type. Change-Id: Ic0eea9db742971db1350a474fbbb5ed24565922b Signed-off-by: Hsiangkai Wang <hsiangkai@gmail.com> Reviewed-on: http://openocd.zylin.com/1571 Tested-by: jenkins Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
This commit is contained in:
parent
cf39210725
commit
2803fa3822
|
@ -72,6 +72,8 @@ static int target_get_gdb_fileio_info_default(struct target *target,
|
||||||
struct gdb_fileio_info *fileio_info);
|
struct gdb_fileio_info *fileio_info);
|
||||||
static int target_gdb_fileio_end_default(struct target *target, int retcode,
|
static int target_gdb_fileio_end_default(struct target *target, int retcode,
|
||||||
int fileio_errno, bool ctrl_c);
|
int fileio_errno, bool ctrl_c);
|
||||||
|
static int target_profiling_default(struct target *target, uint32_t *samples,
|
||||||
|
uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds);
|
||||||
|
|
||||||
/* targets */
|
/* targets */
|
||||||
extern struct target_type arm7tdmi_target;
|
extern struct target_type arm7tdmi_target;
|
||||||
|
@ -1084,6 +1086,17 @@ int target_gdb_fileio_end(struct target *target, int retcode, int fileio_errno,
|
||||||
return target->type->gdb_fileio_end(target, retcode, fileio_errno, ctrl_c);
|
return target->type->gdb_fileio_end(target, retcode, fileio_errno, ctrl_c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int target_profiling(struct target *target, uint32_t *samples,
|
||||||
|
uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds)
|
||||||
|
{
|
||||||
|
if (target->state != TARGET_HALTED) {
|
||||||
|
LOG_WARNING("target %s is not halted", target->cmd_name);
|
||||||
|
return ERROR_TARGET_NOT_HALTED;
|
||||||
|
}
|
||||||
|
return target->type->profiling(target, samples, max_num_samples,
|
||||||
|
num_samples, seconds);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the @c examined flag for the given target.
|
* Reset the @c examined flag for the given target.
|
||||||
* Pure paranoia -- targets are zeroed on allocation.
|
* Pure paranoia -- targets are zeroed on allocation.
|
||||||
|
@ -1173,6 +1186,9 @@ static int target_init_one(struct command_context *cmd_ctx,
|
||||||
if (target->type->gdb_fileio_end == NULL)
|
if (target->type->gdb_fileio_end == NULL)
|
||||||
target->type->gdb_fileio_end = target_gdb_fileio_end_default;
|
target->type->gdb_fileio_end = target_gdb_fileio_end_default;
|
||||||
|
|
||||||
|
if (target->type->profiling == NULL)
|
||||||
|
target->type->profiling = target_profiling_default;
|
||||||
|
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1738,6 +1754,55 @@ static int target_gdb_fileio_end_default(struct target *target,
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int target_profiling_default(struct target *target, uint32_t *samples,
|
||||||
|
uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds)
|
||||||
|
{
|
||||||
|
struct timeval timeout, now;
|
||||||
|
|
||||||
|
gettimeofday(&timeout, NULL);
|
||||||
|
timeval_add_time(&timeout, seconds, 0);
|
||||||
|
|
||||||
|
LOG_INFO("Starting profiling. Halting and resuming the"
|
||||||
|
" target as often as we can...");
|
||||||
|
|
||||||
|
uint32_t sample_count = 0;
|
||||||
|
/* hopefully it is safe to cache! We want to stop/restart as quickly as possible. */
|
||||||
|
struct reg *reg = register_get_by_name(target->reg_cache, "pc", 1);
|
||||||
|
|
||||||
|
int retval = ERROR_OK;
|
||||||
|
for (;;) {
|
||||||
|
target_poll(target);
|
||||||
|
if (target->state == TARGET_HALTED) {
|
||||||
|
uint32_t t = *((uint32_t *)reg->value);
|
||||||
|
samples[sample_count++] = t;
|
||||||
|
/* current pc, addr = 0, do not handle breakpoints, not debugging */
|
||||||
|
retval = target_resume(target, 1, 0, 0, 0);
|
||||||
|
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)
|
||||||
|
break;
|
||||||
|
|
||||||
|
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. %d samples.", sample_count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*num_samples = sample_count;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/* Single aligned words are guaranteed to use 16 or 32 bit access
|
/* Single aligned words are guaranteed to use 16 or 32 bit access
|
||||||
* mode respectively, otherwise data is handled as quickly as
|
* mode respectively, otherwise data is handled as quickly as
|
||||||
* possible
|
* possible
|
||||||
|
@ -3362,7 +3427,7 @@ static void writeString(FILE *f, char *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dump a gmon.out histogram file. */
|
/* Dump a gmon.out histogram file. */
|
||||||
static void writeGmon(uint32_t *samples, uint32_t sampleNum, const char *filename)
|
static void write_gmon(uint32_t *samples, uint32_t sampleNum, const char *filename)
|
||||||
{
|
{
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
FILE *f = fopen(filename, "w");
|
FILE *f = fopen(filename, "w");
|
||||||
|
@ -3451,84 +3516,59 @@ static void writeGmon(uint32_t *samples, uint32_t sampleNum, const char *filenam
|
||||||
COMMAND_HANDLER(handle_profile_command)
|
COMMAND_HANDLER(handle_profile_command)
|
||||||
{
|
{
|
||||||
struct target *target = get_current_target(CMD_CTX);
|
struct target *target = get_current_target(CMD_CTX);
|
||||||
struct timeval timeout, now;
|
|
||||||
|
|
||||||
gettimeofday(&timeout, NULL);
|
|
||||||
if (CMD_ARGC != 2)
|
if (CMD_ARGC != 2)
|
||||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
unsigned offset;
|
|
||||||
|
const uint32_t MAX_PROFILE_SAMPLE_NUM = 10000;
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t num_of_sampels;
|
||||||
|
int retval = ERROR_OK;
|
||||||
|
uint32_t *samples = malloc(sizeof(uint32_t) * MAX_PROFILE_SAMPLE_NUM);
|
||||||
|
if (samples == NULL) {
|
||||||
|
LOG_ERROR("No memory to store samples.");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], offset);
|
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], offset);
|
||||||
|
|
||||||
timeval_add_time(&timeout, offset, 0);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo: Some cores let us sample the PC without the
|
* Some cores let us sample the PC without the
|
||||||
* annoying halt/resume step; for example, ARMv7 PCSR.
|
* annoying halt/resume step; for example, ARMv7 PCSR.
|
||||||
* Provide a way to use that more efficient mechanism.
|
* Provide a way to use that more efficient mechanism.
|
||||||
*/
|
*/
|
||||||
|
retval = target_profiling(target, samples, MAX_PROFILE_SAMPLE_NUM,
|
||||||
|
&num_of_sampels, offset);
|
||||||
|
if (retval != ERROR_OK) {
|
||||||
|
free(samples);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
command_print(CMD_CTX, "Starting profiling. Halting and resuming the target as often as we can...");
|
assert(num_of_sampels <= MAX_PROFILE_SAMPLE_NUM);
|
||||||
|
|
||||||
static const int maxSample = 10000;
|
retval = target_poll(target);
|
||||||
uint32_t *samples = malloc(sizeof(uint32_t)*maxSample);
|
if (retval != ERROR_OK) {
|
||||||
if (samples == NULL)
|
free(samples);
|
||||||
return ERROR_OK;
|
return retval;
|
||||||
|
}
|
||||||
int numSamples = 0;
|
if (target->state == TARGET_RUNNING) {
|
||||||
/* hopefully it is safe to cache! We want to stop/restart as quickly as possible. */
|
retval = target_halt(target);
|
||||||
struct reg *reg = register_get_by_name(target->reg_cache, "pc", 1);
|
if (retval != ERROR_OK) {
|
||||||
|
free(samples);
|
||||||
int retval = ERROR_OK;
|
return retval;
|
||||||
for (;;) {
|
|
||||||
target_poll(target);
|
|
||||||
if (target->state == TARGET_HALTED) {
|
|
||||||
uint32_t t = *((uint32_t *)reg->value);
|
|
||||||
samples[numSamples++] = t;
|
|
||||||
/* current pc, addr = 0, do not handle breakpoints, not debugging */
|
|
||||||
retval = target_resume(target, 1, 0, 0, 0);
|
|
||||||
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);
|
|
||||||
if (retval != ERROR_OK) {
|
|
||||||
free(samples);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
command_print(CMD_CTX, "Target not halted or running");
|
|
||||||
retval = ERROR_OK;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (retval != ERROR_OK)
|
|
||||||
break;
|
|
||||||
|
|
||||||
gettimeofday(&now, NULL);
|
|
||||||
if ((numSamples >= maxSample) || ((now.tv_sec >= timeout.tv_sec)
|
|
||||||
&& (now.tv_usec >= timeout.tv_usec))) {
|
|
||||||
command_print(CMD_CTX, "Profiling completed. %d samples.", numSamples);
|
|
||||||
retval = target_poll(target);
|
|
||||||
if (retval != ERROR_OK) {
|
|
||||||
free(samples);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
if (target->state == TARGET_HALTED) {
|
|
||||||
/* current pc, addr = 0, do not handle
|
|
||||||
* breakpoints, not debugging */
|
|
||||||
target_resume(target, 1, 0, 0, 0);
|
|
||||||
}
|
|
||||||
retval = target_poll(target);
|
|
||||||
if (retval != ERROR_OK) {
|
|
||||||
free(samples);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
writeGmon(samples, numSamples, CMD_ARGV[1]);
|
|
||||||
command_print(CMD_CTX, "Wrote %s", CMD_ARGV[1]);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(samples);
|
|
||||||
|
|
||||||
|
retval = target_poll(target);
|
||||||
|
if (retval != ERROR_OK) {
|
||||||
|
free(samples);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_gmon(samples, num_of_sampels, CMD_ARGV[1]);
|
||||||
|
command_print(CMD_CTX, "Wrote %s", CMD_ARGV[1]);
|
||||||
|
|
||||||
|
free(samples);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -265,6 +265,10 @@ struct target_type {
|
||||||
*/
|
*/
|
||||||
int (*gdb_fileio_end)(struct target *target, int retcode, int fileio_errno, bool ctrl_c);
|
int (*gdb_fileio_end)(struct target *target, int retcode, int fileio_errno, bool ctrl_c);
|
||||||
|
|
||||||
|
/* do target profiling
|
||||||
|
*/
|
||||||
|
int (*profiling)(struct target *target, uint32_t *samples,
|
||||||
|
uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* TARGET_TYPE_H */
|
#endif /* TARGET_TYPE_H */
|
||||||
|
|
Loading…
Reference in New Issue