Restore mwh/mwb functionality.
[openocd.git] / src / target / target.c
index 6aee09832e3983362d278c3caef9394c66cf7fed..fce1cd42dff740bab04c97e1152b4053350a51ea 100644 (file)
@@ -60,9 +60,9 @@
 #define DEFAULT_HALT_TIMEOUT 5000
 
 static int target_read_buffer_default(struct target *target, uint32_t address,
-               uint32_t size, uint8_t *buffer);
+               uint32_t count, uint8_t *buffer);
 static int target_write_buffer_default(struct target *target, uint32_t address,
-               uint32_t size, const uint8_t *buffer);
+               uint32_t count, const uint8_t *buffer);
 static int target_array2mem(Jim_Interp *interp, struct target *target,
                int argc, Jim_Obj * const *argv);
 static int target_mem2array(Jim_Interp *interp, struct target *target,
@@ -72,6 +72,8 @@ static int target_get_gdb_fileio_info_default(struct target *target,
                struct gdb_fileio_info *fileio_info);
 static int target_gdb_fileio_end_default(struct target *target, int retcode,
                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 */
 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);
 }
 
+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.
  * 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)
                target->type->gdb_fileio_end = target_gdb_fileio_end_default;
 
+       if (target->type->profiling == NULL)
+               target->type->profiling = target_profiling_default;
+
        return ERROR_OK;
 }
 
@@ -1738,6 +1754,55 @@ static int target_gdb_fileio_end_default(struct target *target,
        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
  * mode respectively, otherwise data is handled as quickly as
  * possible
@@ -1766,50 +1831,37 @@ int target_write_buffer(struct target *target, uint32_t address, uint32_t size,
        return target->type->write_buffer(target, address, size, buffer);
 }
 
-static int target_write_buffer_default(struct target *target, uint32_t address, uint32_t size, const uint8_t *buffer)
+static int target_write_buffer_default(struct target *target, uint32_t address, uint32_t count, const uint8_t *buffer)
 {
-       int retval = ERROR_OK;
-
-       if (((address % 2) == 0) && (size == 2))
-               return target_write_memory(target, address, 2, 1, buffer);
-
-       /* handle unaligned head bytes */
-       if (address % 4) {
-               uint32_t unaligned = 4 - (address % 4);
+       uint32_t size;
 
-               if (unaligned > size)
-                       unaligned = size;
-
-               retval = target_write_memory(target, address, 1, unaligned, buffer);
-               if (retval != ERROR_OK)
-                       return retval;
-
-               buffer += unaligned;
-               address += unaligned;
-               size -= unaligned;
-       }
-
-       /* handle aligned words */
-       if (size >= 4) {
-               int aligned = size - (size % 4);
-
-               retval = target_write_memory(target, address, 4, aligned / 4, buffer);
-               if (retval != ERROR_OK)
-                       return retval;
-
-               buffer += aligned;
-               address += aligned;
-               size -= aligned;
+       /* Align up to maximum 4 bytes. The loop condition makes sure the next pass
+        * will have something to do with the size we leave to it. */
+       for (size = 1; size < 4 && count >= size * 2 + (address & size); size *= 2) {
+               if (address & size) {
+                       int retval = target_write_memory(target, address, size, 1, buffer);
+                       if (retval != ERROR_OK)
+                               return retval;
+                       address += size;
+                       count -= size;
+                       buffer += size;
+               }
        }
 
-       /* handle tail writes of less than 4 bytes */
-       if (size > 0) {
-               retval = target_write_memory(target, address, 1, size, buffer);
-               if (retval != ERROR_OK)
-                       return retval;
+       /* Write the data with as large access size as possible. */
+       for (; size > 0; size /= 2) {
+               uint32_t aligned = count - count % size;
+               if (aligned > 0) {
+                       int retval = target_write_memory(target, address, size, aligned / size, buffer);
+                       if (retval != ERROR_OK)
+                               return retval;
+                       address += aligned;
+                       count -= aligned;
+                       buffer += aligned;
+               }
        }
 
-       return retval;
+       return ERROR_OK;
 }
 
 /* Single aligned words are guaranteed to use 16 or 32 bit access
@@ -1840,58 +1892,34 @@ int target_read_buffer(struct target *target, uint32_t address, uint32_t size, u
        return target->type->read_buffer(target, address, size, buffer);
 }
 
-static int target_read_buffer_default(struct target *target, uint32_t address, uint32_t size, uint8_t *buffer)
+static int target_read_buffer_default(struct target *target, uint32_t address, uint32_t count, uint8_t *buffer)
 {
-       int retval = ERROR_OK;
-
-       if (((address % 2) == 0) && (size == 2))
-               return target_read_memory(target, address, 2, 1, buffer);
-
-       /* handle unaligned head bytes */
-       if (address % 4) {
-               uint32_t unaligned = 4 - (address % 4);
-
-               if (unaligned > size)
-                       unaligned = size;
-
-               retval = target_read_memory(target, address, 1, unaligned, buffer);
-               if (retval != ERROR_OK)
-                       return retval;
-
-               buffer += unaligned;
-               address += unaligned;
-               size -= unaligned;
-       }
-
-       /* handle aligned words */
-       if (size >= 4) {
-               int aligned = size - (size % 4);
+       uint32_t size;
 
-               retval = target_read_memory(target, address, 4, aligned / 4, buffer);
-               if (retval != ERROR_OK)
-                       return retval;
-
-               buffer += aligned;
-               address += aligned;
-               size -= aligned;
+       /* Align up to maximum 4 bytes. The loop condition makes sure the next pass
+        * will have something to do with the size we leave to it. */
+       for (size = 1; size < 4 && count >= size * 2 + (address & size); size *= 2) {
+               if (address & size) {
+                       int retval = target_read_memory(target, address, size, 1, buffer);
+                       if (retval != ERROR_OK)
+                               return retval;
+                       address += size;
+                       count -= size;
+                       buffer += size;
+               }
        }
 
-       /*prevent byte access when possible (avoid AHB access limitations in some cases)*/
-       if (size        >= 2) {
-               int aligned = size - (size % 2);
-               retval = target_read_memory(target, address, 2, aligned / 2, buffer);
-               if (retval != ERROR_OK)
-                       return retval;
-
-               buffer += aligned;
-               address += aligned;
-               size -= aligned;
-       }
-       /* handle tail writes of less than 4 bytes */
-       if (size > 0) {
-               retval = target_read_memory(target, address, 1, size, buffer);
-               if (retval != ERROR_OK)
-                       return retval;
+       /* Read the data with as large access size as possible. */
+       for (; size > 0; size /= 2) {
+               uint32_t aligned = count - count % size;
+               if (aligned > 0) {
+                       int retval = target_read_memory(target, address, size, aligned / size, buffer);
+                       if (retval != ERROR_OK)
+                               return retval;
+                       address += aligned;
+                       count -= aligned;
+                       buffer += aligned;
+               }
        }
 
        return ERROR_OK;
@@ -2716,12 +2744,6 @@ COMMAND_HANDLER(handle_md_command)
 typedef int (*target_write_fn)(struct target *target,
                uint32_t address, uint32_t size, uint32_t count, const uint8_t *buffer);
 
-static int target_write_memory_fast(struct target *target,
-               uint32_t address, uint32_t size, uint32_t count, const uint8_t *buffer)
-{
-       return target_write_buffer(target, address, size * count, buffer);
-}
-
 static int target_fill_mem(struct target *target,
                uint32_t address,
                target_write_fn fn,
@@ -2786,7 +2808,7 @@ COMMAND_HANDLER(handle_mw_command)
                CMD_ARGV++;
                fn = target_write_phys_memory;
        } else
-               fn = target_write_memory_fast;
+               fn = target_write_memory;
        if ((CMD_ARGC < 2) || (CMD_ARGC > 3))
                return ERROR_COMMAND_SYNTAX_ERROR;
 
@@ -3398,8 +3420,11 @@ static void writeString(FILE *f, char *s)
        writeData(f, s, strlen(s));
 }
 
+typedef unsigned char UNIT[2];  /* unit of profiling */
+
 /* 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,
+               bool with_range, uint32_t start_address, uint32_t end_address)
 {
        uint32_t i;
        FILE *f = fopen(filename, "w");
@@ -3415,33 +3440,50 @@ static void writeGmon(uint32_t *samples, uint32_t sampleNum, const char *filenam
        writeData(f, &zero, 1);
 
        /* figure out bucket size */
-       uint32_t min = samples[0];
-       uint32_t max = samples[0];
-       for (i = 0; i < sampleNum; i++) {
-               if (min > samples[i])
-                       min = samples[i];
-               if (max < samples[i])
-                       max = samples[i];
+       uint32_t min;
+       uint32_t max;
+       if (with_range) {
+               min = start_address;
+               max = end_address;
+       } else {
+               min = samples[0];
+               max = samples[0];
+               for (i = 0; i < sampleNum; i++) {
+                       if (min > samples[i])
+                               min = samples[i];
+                       if (max < samples[i])
+                               max = samples[i];
+               }
+
+               /* max should be (largest sample + 1)
+                * Refer to binutils/gprof/hist.c (find_histogram_for_pc) */
+               max++;
        }
 
-       int addressSpace = (max - min + 1);
+       int addressSpace = max - min;
        assert(addressSpace >= 2);
 
-       static const uint32_t maxBuckets = 16 * 1024; /* maximum buckets. */
-       uint32_t length = addressSpace;
-       if (length > maxBuckets)
-               length = maxBuckets;
-       int *buckets = malloc(sizeof(int)*length);
+       /* FIXME: What is the reasonable number of buckets?
+        * The profiling result will be more accurate if there are enough buckets. */
+       static const uint32_t maxBuckets = 128 * 1024; /* maximum buckets. */
+       uint32_t numBuckets = addressSpace / sizeof(UNIT);
+       if (numBuckets > maxBuckets)
+               numBuckets = maxBuckets;
+       int *buckets = malloc(sizeof(int) * numBuckets);
        if (buckets == NULL) {
                fclose(f);
                return;
        }
-       memset(buckets, 0, sizeof(int) * length);
+       memset(buckets, 0, sizeof(int) * numBuckets);
        for (i = 0; i < sampleNum; i++) {
                uint32_t address = samples[i];
+
+               if ((address < min) || (max <= address))
+                       continue;
+
                long long a = address - min;
-               long long b = length - 1;
-               long long c = addressSpace - 1;
+               long long b = numBuckets;
+               long long c = addressSpace;
                int index_t = (a * b) / c; /* danger!!!! int32 overflows */
                buckets[index_t]++;
        }
@@ -3449,7 +3491,7 @@ static void writeGmon(uint32_t *samples, uint32_t sampleNum, const char *filenam
        /* append binary memory gmon.out &profile_hist_hdr ((char*)&profile_hist_hdr + sizeof(struct gmon_hist_hdr)) */
        writeLong(f, min);                      /* low_pc */
        writeLong(f, max);                      /* high_pc */
-       writeLong(f, length);           /* # of samples */
+       writeLong(f, numBuckets);       /* # of buckets */
        writeLong(f, 100);                      /* KLUDGE! We lie, ca. 100Hz best case. */
        writeString(f, "seconds");
        for (i = 0; i < (15-strlen("seconds")); i++)
@@ -3458,9 +3500,9 @@ static void writeGmon(uint32_t *samples, uint32_t sampleNum, const char *filenam
 
        /*append binary memory gmon.out profile_hist_data (profile_hist_data + profile_hist_hdr.hist_size) */
 
-       char *data = malloc(2 * length);
+       char *data = malloc(2 * numBuckets);
        if (data != NULL) {
-               for (i = 0; i < length; i++) {
+               for (i = 0; i < numBuckets; i++) {
                        int val;
                        val = buckets[i];
                        if (val > 65535)
@@ -3469,7 +3511,7 @@ static void writeGmon(uint32_t *samples, uint32_t sampleNum, const char *filenam
                        data[i * 2 + 1] = (val >> 8) & 0xff;
                }
                free(buckets);
-               writeData(f, data, length * 2);
+               writeData(f, data, numBuckets * 2);
                free(data);
        } else
                free(buckets);
@@ -3482,84 +3524,69 @@ static void writeGmon(uint32_t *samples, uint32_t sampleNum, const char *filenam
 COMMAND_HANDLER(handle_profile_command)
 {
        struct target *target = get_current_target(CMD_CTX);
-       struct timeval timeout, now;
 
-       gettimeofday(&timeout, NULL);
-       if (CMD_ARGC != 2)
+       if ((CMD_ARGC != 2) && (CMD_ARGC != 4))
                return ERROR_COMMAND_SYNTAX_ERROR;
-       unsigned offset;
-       COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], offset);
 
-       timeval_add_time(&timeout, offset, 0);
+       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);
 
        /**
-        * @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.
         * 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...");
-
-       static const int maxSample = 10000;
-       uint32_t *samples = malloc(sizeof(uint32_t)*maxSample);
-       if (samples == NULL)
-               return ERROR_OK;
-
-       int numSamples = 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);
+       assert(num_of_sampels <= MAX_PROFILE_SAMPLE_NUM);
 
-       int retval = ERROR_OK;
-       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;
+       retval = target_poll(target);
+       if (retval != ERROR_OK) {
+               free(samples);
+               return retval;
+       }
+       if (target->state == TARGET_RUNNING) {
+               retval = target_halt(target);
+               if (retval != ERROR_OK) {
+                       free(samples);
+                       return retval;
                }
-               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;
-               }
+       retval = target_poll(target);
+       if (retval != ERROR_OK) {
+               free(samples);
+               return retval;
+       }
+
+       uint32_t start_address = 0;
+       uint32_t end_address = 0;
+       bool with_range = false;
+       if (CMD_ARGC == 4) {
+               with_range = true;
+               COMMAND_PARSE_NUMBER(uint, CMD_ARGV[2], start_address);
+               COMMAND_PARSE_NUMBER(uint, CMD_ARGV[3], end_address);
        }
-       free(samples);
 
+       write_gmon(samples, num_of_sampels, CMD_ARGV[1],
+                       with_range, start_address, end_address);
+       command_print(CMD_CTX, "Wrote %s", CMD_ARGV[1]);
+
+       free(samples);
        return retval;
 }
 
@@ -4342,7 +4369,7 @@ static int jim_target_mw(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
        }
 
        target_write_fn fn;
-       fn = target_write_memory_fast;
+       fn = target_write_memory;
 
        int e;
        if (strcmp(Jim_GetString(argv[1], NULL), "phys") == 0) {
@@ -5554,7 +5581,7 @@ static const struct command_registration target_exec_command_handlers[] = {
                .name = "profile",
                .handler = handle_profile_command,
                .mode = COMMAND_EXEC,
-               .usage = "seconds filename",
+               .usage = "seconds filename [start end]",
                .help = "profiling samples the CPU PC",
        },
        /** @todo don't register virt2phys() unless target supports it */

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)