This is an automated email from Gerrit. "Samuel Obuch <[email protected]>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9767
-- gerrit commit 417d8051641b5baad070a880a2a8ba98ee1375fb Author: Samuel Obuch <[email protected]> Date: Wed May 20 17:17:30 2026 +0200 target/profile: start new histogram instead of dropping samples Dropping samples after reaching the UINT16_MAX limit can limit usefulness especially with targets capable of high sample rate. Instead, the sorted sequence of samples can be further pre-processed to count number of samples in each bucket, and in case of a bucket overrun use a separate one-bucket histograms. Change-Id: Ia1167ceb0225c1c90ec3bf5077c38a7ad23c3bb6 Signed-off-by: Samuel Obuch <[email protected]> diff --git a/src/target/target.c b/src/target/target.c index 1b2bc8191b..c8e5de7f89 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -4225,7 +4225,7 @@ static void write_string(FILE *f, char *s) typedef unsigned char UNIT[2]; /* unit of profiling */ -static void write_gmon_hist(FILE *f, const uint32_t *samples, uint32_t sample_num, +static void write_gmon_hist(FILE *f, const uint32_t *samples, const uint32_t *counts, uint32_t sample_num, float sample_rate, struct target *target) { uint32_t min = samples[0]; @@ -4257,21 +4257,11 @@ static void write_gmon_hist(FILE *f, const uint32_t *samples, uint32_t sample_nu write_string(f, "s"); /*append binary memory gmon.out profile_hist_data (profile_hist_data + profile_hist_hdr.hist_size) */ - bool saturated_once = false; for (uint32_t i = 0, bidx = 0; bidx < address_space; bidx += sizeof(UNIT)) { - uint32_t val = i; + uint32_t val = 0; uint32_t bmax = min + bidx + sizeof(UNIT); while (i < sample_num && samples[i] < bmax) - ++i; - val = i - val; - - if (val > UINT16_MAX) { - val = UINT16_MAX; - if (!saturated_once) - LOG_WARNING("profiler bucket saturated, will read as 65535"); - saturated_once = true; - } - + val += counts[i++]; uint8_t data[2]; h_u16_to_le(data, val); write_data(f, data, 2); @@ -4279,10 +4269,9 @@ static void write_gmon_hist(FILE *f, const uint32_t *samples, uint32_t sample_nu } /* Dump a gmon.out histogram file. */ -static void write_gmon(const uint32_t *samples, uint32_t sample_num, const char *filename, - struct target *target, uint32_t duration_ms) +static void write_gmon(const uint32_t *samples, const uint32_t *counts, uint32_t sample_num, const char *filename, + struct target *target, uint32_t duration_ms, float sample_rate) { - float sample_rate = sample_num / (duration_ms / 1000.0); FILE *f = fopen(filename, "wb"); if (!f) return; @@ -4293,18 +4282,30 @@ static void write_gmon(const uint32_t *samples, uint32_t sample_num, const char write_long(f, 0, target); /* padding */ while (sample_num) { - /* if address gap exceeds this, make another histogram */ - const uint32_t MAX_GAP = 32; - - /* figure out bucket size */ - uint32_t max = samples[0]; uint32_t this_pass = 1; - while (this_pass < sample_num && samples[this_pass] - max < MAX_GAP) - max = samples[this_pass++]; + const uint32_t bucket_max = UINT16_MAX; + + if (counts[0] > bucket_max) { + /* if the next sample has more than UINT16_MAX hits, write it in one-address histograms */ + uint32_t remaining = counts[0]; + while (remaining > bucket_max) { + write_gmon_hist(f, samples, &bucket_max, 1, sample_rate, target); + remaining -= bucket_max; + } + write_gmon_hist(f, samples, &remaining, 1, sample_rate, target); + } else { + /* if address gap exceeds this, make another histogram */ + const uint32_t MAX_GAP = 32; - write_gmon_hist(f, samples, this_pass, sample_rate, target); + /* figure out bucket size */ + uint32_t max = samples[0]; + while (this_pass < sample_num && samples[this_pass] - max < MAX_GAP && counts[this_pass] <= bucket_max) + max = samples[this_pass++]; + write_gmon_hist(f, samples, counts, this_pass, sample_rate, target); + } samples += this_pass; + counts += this_pass; sample_num -= this_pass; } @@ -4428,9 +4429,29 @@ COMMAND_HANDLER(handle_profile_command) qsort(samples, num_of_samples, sizeof(samples[0]), compare_pc32); - write_gmon(samples, num_of_samples, CMD_ARGV[1], target, duration_ms); - command_print(CMD, "Wrote %s", CMD_ARGV[1]); + uint32_t *counts = malloc(sizeof(uint32_t) * num_of_samples); + if (!counts) { + LOG_ERROR("No memory to store sample counts."); + free(samples); + return ERROR_FAIL; + } + counts[0] = 1; + uint32_t num_unique_samples = 1; + for (uint32_t in = 1; in < num_of_samples; ++in) { + uint32_t sample = samples[in]; + if (sample != samples[num_unique_samples - 1]) { + samples[num_unique_samples] = sample; + counts[num_unique_samples] = 1; + num_unique_samples++; + } else { + counts[num_unique_samples - 1]++; + } + } + float sample_rate = num_of_samples / (duration_ms / 1000.0); + write_gmon(samples, counts, num_unique_samples, CMD_ARGV[1], target, duration_ms, sample_rate); + command_print(CMD, "Wrote %s", CMD_ARGV[1]); + free(counts); free(samples); return ERROR_OK; } --
