Skip to content

Commit 13f1d37

Browse files
Automatic startup, write to Stream, example
1 parent 5c147e5 commit 13f1d37

File tree

5 files changed

+147
-52
lines changed

5 files changed

+147
-52
lines changed

cores/rp2040/RP2040Support.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1919
*/
2020

21+
#include <Arduino.h>
22+
#include <pico/runtime.h>
23+
2124
#ifdef PICO_RP2040
2225

23-
#include <Arduino.h>
2426
#include <hardware/structs/psm.h>
2527

2628
extern "C" void boot_double_tap_check();
@@ -35,3 +37,17 @@ void RP2040::enableDoubleResetBootloader() {
3537
}
3638

3739
#endif
40+
41+
#ifdef __PROFILE
42+
Stream *__profileFile;
43+
int __writeProfileCB(const void *data, int len) {
44+
return __profileFile->write((const char *)data, len);
45+
}
46+
47+
#ifdef __PROFILE
48+
extern "C" void runtime_init_setup_profiling();
49+
#define PICO_RUNTIME_INIT_PROFILING "11011" // Towards the end, after PSRAM
50+
PICO_RUNTIME_INIT_FUNC_RUNTIME(runtime_init_setup_profiling, PICO_RUNTIME_INIT_PROFILING);
51+
#endif
52+
53+
#endif

cores/rp2040/RP2040Support.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1919
*/
2020

21+
#pragma once
22+
2123
#include <hardware/clocks.h>
2224
#include <hardware/irq.h>
2325
#include <hardware/pio.h>
@@ -45,6 +47,13 @@
4547

4648
extern "C" volatile bool __otherCoreIdled;
4749

50+
extern "C" {
51+
#ifdef __PROFILE
52+
typedef int (*profileWriteCB)(const void *data, int len);
53+
extern void _writeProfile(profileWriteCB writeCB);
54+
#endif
55+
}
56+
4857
class _MFIFO {
4958
public:
5059
_MFIFO() { /* noop */ };
@@ -473,6 +482,16 @@ class RP2040 {
473482
#endif
474483
}
475484

485+
#ifdef __PROFILE
486+
void writeProfiling(Stream *f) {
487+
extern Stream *__profileFile;
488+
extern int __writeProfileCB(const void *data, int len);
489+
__profileFile = f;
490+
_writeProfile(__writeProfileCB);
491+
}
492+
#endif
493+
494+
476495

477496
private:
478497
static void _SystickHandler() {

cores/rp2040/gprof_gmon.c

Lines changed: 20 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,17 @@
4949
#include <stdint.h>
5050
#include <string.h>
5151

52-
52+
// Frequency of sampling
53+
#ifndef GMON_HZ
54+
#define GMON_HZ 10000
55+
#endif
5356

5457
/*
5558
fraction of text space to allocate for histogram counters here, 1/2
5659
*/
60+
#ifndef HISTFRACTION
5761
#define HISTFRACTION 8
62+
#endif
5863

5964
/*
6065
Fraction of text space to allocate for from hash buckets.
@@ -84,12 +89,16 @@
8489
profiling data structures without (in practice) sacrificing
8590
any granularity.
8691
*/
92+
#ifndef HASHFRACTION
8793
#define HASHFRACTION 2
94+
#endif
8895

8996
/*
9097
percent of text space to allocate for tostructs with a minimum.
9198
*/
99+
#ifndef ARCDENSITY
92100
#define ARCDENSITY 2 /* this is in percentage, relative to text size! */
101+
#endif
93102
#define MINARCS 50
94103
#define MAXARCS ((1 << (8 * sizeof(HISTCOUNTER))) - 2)
95104

@@ -101,7 +110,7 @@
101110
#define GMON_PROF_ERROR 2
102111
#define GMON_PROF_OFF 3
103112

104-
void _mcleanup(void); /* routine to be called to write gmon.out file */
113+
//void _mcleanup(void); /* routine to be called to write gmon.out file */
105114

106115

107116

@@ -195,7 +204,6 @@ static struct profinfo prof = {
195204
PROFILE_NOT_INIT, 0, 0, 0, 0
196205
};
197206
extern volatile bool __otherCoreIdled;
198-
static int __profileHz = 0;
199207

200208
/* convert an addr to an index */
201209
#define PROFIDX(pc, base, scale) \
@@ -290,22 +298,17 @@ int __no_inline_not_in_flash_func(profil)(char *samples, size_t size, size_t off
290298
#define PICO_RUNTIME_INIT_PROFILING "11011" // Towards the end, after PSRAM
291299
#if defined(__riscv)
292300
void runtime_init_setup_profiling() {
293-
__profileHz = 10000;
294301
// TODO - is there an equivalent? Or do we need to build a timer IRQ here?
295302
}
296303
#else
297304
#include <hardware/exception.h>
298305
#include <hardware/structs/systick.h>
299306
void runtime_init_setup_profiling() {
300-
__profileHz = 10000;
301307
exception_set_exclusive_handler(SYSTICK_EXCEPTION, _SystickHandler);
302308
systick_hw->csr = 0x7;
303-
systick_hw->rvr = (F_CPU / __profileHz) - 1;
309+
systick_hw->rvr = (F_CPU / GMON_HZ) - 1;
304310
}
305311
#endif
306-
#ifdef __PROFILE
307-
PICO_RUNTIME_INIT_FUNC_RUNTIME(runtime_init_setup_profiling, PICO_RUNTIME_INIT_PROFILING);
308-
#endif
309312

310313

311314
#define MINUS_ONE_P (-1)
@@ -393,54 +396,28 @@ void __no_inline_not_in_flash_func(monstartup)(size_t lowpc, size_t highpc) {
393396
#ifndef O_BINARY
394397
#define O_BINARY 0
395398
#endif
396-
extern int __profileHz;
397399

398-
void _mcleanup(void) {
399-
static const char gmon_out[] = "gmon.out";
400-
int fd;
401-
int hz;
400+
// Ensure matches definition in rp2040support
401+
typedef int (*profileWriteCB)(const void *data, int len);
402+
void _writeProfile(profileWriteCB writeCB) {
402403
int fromindex;
403404
int endfrom;
404405
size_t frompc;
405406
int toindex;
406407
struct rawarc rawarc;
407408
struct gmonparam *p = &_gmonparam;
408409
struct gmonhdr gmonhdr, *hdr;
409-
const char *proffile;
410-
#ifdef DEBUG
411-
int log, len;
412-
char dbuf[200];
413-
#endif
414410

415-
if (p->state == GMON_PROF_ERROR) {
416-
ERR("_mcleanup: tos overflow\n");
417-
}
418-
hz = __profileHz;//PROF_HZ;
419411
moncontrol(0); /* stop */
420-
proffile = gmon_out;
421-
fd = open(proffile, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0666);
422-
if (fd < 0) {
423-
perror(proffile);
424-
return;
425-
}
426-
#ifdef DEBUG
427-
log = open("gmon.log", O_CREAT | O_TRUNC | O_WRONLY, 0664);
428-
if (log < 0) {
429-
perror("mcount: gmon.log");
430-
return;
431-
}
432-
len = sprintf(dbuf, "[mcleanup1] kcount 0x%x ssiz %d\n",
433-
p->kcount, p->kcountsize);
434-
write(log, dbuf, len);
435-
#endif
412+
436413
hdr = (struct gmonhdr *)&gmonhdr;
437414
hdr->lpc = p->lowpc;
438415
hdr->hpc = p->highpc;
439416
hdr->ncnt = p->kcountsize + sizeof(gmonhdr);
440417
hdr->version = GMONVERSION;
441-
hdr->profrate = hz;
442-
write(fd, (char *)hdr, sizeof * hdr);
443-
write(fd, p->kcount, p->kcountsize);
418+
hdr->profrate = GMON_HZ;
419+
writeCB((void *)hdr, sizeof * hdr);
420+
writeCB((void *)p->kcount, p->kcountsize);
444421
endfrom = p->fromssize / sizeof(*p->froms);
445422
for (fromindex = 0; fromindex < endfrom; fromindex++) {
446423
if (p->froms[fromindex] == 0) {
@@ -449,20 +426,12 @@ void _mcleanup(void) {
449426
frompc = p->lowpc;
450427
frompc += fromindex * HASHFRACTION * sizeof(*p->froms);
451428
for (toindex = p->froms[fromindex]; toindex != 0; toindex = p->tos[toindex].link) {
452-
#ifdef DEBUG
453-
len = sprintf(dbuf,
454-
"[mcleanup2] frompc 0x%x selfpc 0x%x count %d\n",
455-
frompc, p->tos[toindex].selfpc,
456-
p->tos[toindex].count);
457-
write(log, dbuf, len);
458-
#endif
459429
rawarc.raw_frompc = frompc;
460430
rawarc.raw_selfpc = GETSELFPC(&p->tos[toindex]);
461431
rawarc.raw_count = GETCOUNT(&p->tos[toindex]);
462-
write(fd, &rawarc, sizeof rawarc);
432+
writeCB((void *)&rawarc, sizeof rawarc);
463433
}
464434
}
465-
close(fd);
466435
}
467436

468437
/*

keywords.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ getUsedPSRAMHeap KEYWORD2
6565
getTotalPSRAMHeap KEYWORD2
6666
getTotalPSRAM KEYWORD2
6767

68+
writeProfiling KEYWORD2
69+
6870
getChipID KEYWORD2
6971

7072
hwrand32 KEYWORD2
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// This example should be run with profiling enabled from the IDE and
2+
// under GDB/OpenOCD. It uses semihosting to write a gmon.out file
3+
// the host system with the profiled application results.
4+
//
5+
// Semihosting **ONLY** works with an OpenOCD and GDB setup. If you build
6+
// and run a semihosting app without GDB connected, it **WILL CRASH**
7+
//
8+
// Start OpenOCD normally, but leave the terminal window visible because
9+
// is it OpenOCD, not GDB, which will display the semihosting output.
10+
// OpenOCD will also create files in the current working directory, so
11+
// be sure it is a place you can find and write to.
12+
//
13+
// In GDB,connect to OpenOCD and then enable semihosting
14+
// (gdb) target extended-remote localhost:3333
15+
// (gdb) monitor arm semihosting enable
16+
// (gdb) file /path/to/sketch.ino.elf
17+
// (gdb) load
18+
//
19+
// Run the app from GDB and watch OpenOCD, it will display messages when
20+
// the app is done and "gmon.out" is on the host system.
21+
//
22+
// (gdb) run
23+
// .. pop to OpenOCD window
24+
// [OpenOCD] BEGIN
25+
// [OpenOCD] Result = 2417697592
26+
// [OpenOCD] Writing GMON.OUT
27+
// [OpenOCD] END
28+
//
29+
// From command line, decode the gmon.out using the ELF and gprof tool
30+
//
31+
// $ /path/to/arm-none-eabi/bin/arm-none-eabi-gprof /path/to/sketch.ino.elf /path/to/gmon.out | less
32+
// Flat profile:
33+
//
34+
// Each sample counts as 0.0001 seconds.
35+
// % cumulative self self total
36+
// time seconds seconds calls ms/call ms/call name
37+
// 50.56 1.74 1.74 3500020 0.00 0.00 __wrap___getreent
38+
// 24.05 2.57 0.83 rand
39+
// 8.32 2.86 0.29 5 57.36 57.36 fcn1(unsigned long)
40+
// ...
41+
// index % time self children called name
42+
// <spontaneous>
43+
// [1] 74.6 0.83 1.74 rand [1]
44+
// 1.74 0.00 3500000/3500020 __wrap___getreent [2]
45+
// -----------------------------------------------
46+
// 0.00 0.00 1/3500020 realloc [106]
47+
// 0.00 0.00 3/3500020 vsnprintf [54]
48+
// 0.00 0.00 7/3500020 srand [7]
49+
// 0.00 0.00 9/3500020 malloc [105]
50+
// 1.74 0.00 3500000/3500020 rand [1]
51+
// ...
52+
53+
#ifdef __riscv
54+
void setup() {
55+
// No semihosting for RISCV yet
56+
}
57+
void loop() {
58+
}
59+
#else
60+
61+
#include <SemiFS.h>
62+
63+
uint32_t fcn1(uint32_t st) {
64+
srand(st);
65+
for (int i = 0; i < 500000; i++) st += rand();
66+
return st;
67+
}
68+
69+
uint32_t fcn2(uint32_t st) {
70+
srand(st * st);
71+
for (int i = 0; i < 500000; i++) st += rand();
72+
return st;
73+
}
74+
75+
void setup() {
76+
SerialSemi.printf("BEGIN\n");
77+
SerialSemi.printf("Result = %lu\n", fcn2(fcn2(fcn1(3)) * fcn1(fcn1(fcn1(fcn1(2))))));
78+
SerialSemi.printf("Writing GMON.OUT\n");
79+
SemiFS.begin();
80+
File gmon = SemiFS.open("gmon.out", "w");
81+
rp2040.writeProfiling(&gmon);
82+
gmon.close();
83+
SerialSemi.printf("END\n");
84+
}
85+
86+
void loop() {
87+
}
88+
89+
#endif

0 commit comments

Comments
 (0)