SRAM size compile time check/report.

Here is a fix I'm using, it uses avr-objdump to determine the sram usage (limited to 1024 bytes) and sketch size, it prints out the size of the statically allocated ram after the sketch size and prints a warning if it exceeds a certain threshold, and throws an error if max ram is exceeded. Handy for quickly reminding users using too much data in ram (easy to do) to consider using PROGMEM.

Enjoy :slight_smile:

here is the replacement size function I'm using in Sketch.java:

  protected void size(String buildPath, String suggestedClassName)
    throws RunnerException {
    long size = 0;
    long maxsize = Preferences.getInteger("boards." + Preferences.get("board") + ".upload.maximum_size");
    
    String s =  Preferences.get("boards." + Preferences.get("board") + ".upload.maximum_ram_size");
    long maxram = s==null?1024:Integer.parseInt(s);

    s =  Preferences.get("boards." + Preferences.get("board") + ".upload.warning_ram_size");
    long warningram = s==null?maxram/2:Integer.parseInt(s);

    Sizer sizer = new Sizer(buildPath, suggestedClassName);
    try {
      size = sizer.computeSize();
      System.out.println("Binary sketch size: " + size + " bytes (of a " +
        maxsize + " byte maximum)");
      System.out.println("Chip memory sram: " + sizer.data +
        " bytes (of a " +maxram+ " byte maximum)");      
    } catch (RunnerException e) {
      System.err.println("Couldn't determine program size: " + e.getMessage());
    }

   
    if (sizer.data > maxram)
          throw new RunnerException(
          "Allowable chip memory exceeded; see http://www.arduino.cc/en/Reference/PROGMEM to reduce ram size");

    if (sizer.data > warningram)
        System.err.println("Warning Large amount of chip memory used.  Consider using PROGMEM, http://www.arduino.cc/en/Reference/PROGMEM, to reduce ram size ");
    
    if (size > maxsize)
      throw new RunnerException(
        "Sketch too big; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing it.");
  }

And here is the replacement Sizer.java

package processing.app;

import java.io.*;
import java.util.*;

public class Sizer implements MessageConsumer {
  private String buildPath, sketchName;
  private String firstLine;
  private long size;
  private RunnerException exception;

  public Sizer(String buildPath, String sketchName) {
    this.buildPath = buildPath;
    this.sketchName = sketchName;
  }
  
  public long computeSize() throws RunnerException {
    String userdir = System.getProperty("user.dir") + File.separator;
      String avrBasePath;
    if(Base.isMacOS()) {
          avrBasePath = new String("hardware/tools/avr/bin/"); 
    }
    else if(Base.isLinux()) {
          avrBasePath = new String("");           
    }
    else {
          avrBasePath = new String(userdir + "hardware/tools/avr/bin/"); 
    }
    String commandSize[] = new String[] {
      avrBasePath + "avr-objdump",
      "-h",
      ""
    };
    
    commandSize[2] = buildPath + File.separator + sketchName + ".elf";

    try {
      exception = null;
      size = -1;
      firstLine = null;
      Process process = Runtime.getRuntime().exec(commandSize);
      new MessageSiphon(process.getInputStream(), this);
      new MessageSiphon(process.getErrorStream(), this);
      boolean running = true;
      while(running) {
        try {
          process.waitFor();
          running = false;
        } catch (InterruptedException intExc) { }
      }
    } catch (Exception e) {
      // The default Throwable.toString() never returns null, but apparently
      // some sub-class has overridden it to do so, thus we need to check for
      // it.  See: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1166589459
      exception = new RunnerException(
        (e.toString() == null) ? e.getClass().getName() : e.toString());
    }
    
    if (exception != null)
      throw exception;
      
    if (size == -1)
      throw new RunnerException(firstLine);
      
    return size+1;
  }
  public int data = 0;
  public void message(String s) {
    if (firstLine == null)
      firstLine = s;
    else {
      size+=checkTag(s," .text ");      
      data+=checkTag(s," .data ");      
      data+=checkTag(s," .bss ");      
//        exception = new RunnerException(e.toString());
      
    }
  }
  
private int checkTag(String s, String tag){
  int size=0;      
  int p = s.indexOf(tag);
  if(p != -1){
        s=s.substring(p+6).trim();
      p=s.indexOf(" ");
      if(p != -1)
          size = Integer.parseInt(s.substring(0,p).trim(),16);
  }
  return size;
}
  
}

Note: the original Sizer used avr-size, but it did not display any sram info on my XP machine.

Dave.

Cool, Dave. Nice work.

Mikal

Thanks Mikal :slight_smile:

As an afterthought, I'm thinking it should direct users to here instead:

Awesome. I've added an item to my todo list to integrate this code.

Cool :slight_smile: Thx David!

So I'm pretty new to tinkering with this sort of thing, but it would definitely be a help to me to incorporate this feature in my IDE. Can anyone post or direct to a step-by-step method of incorporating this capability into my current Arduino IDE?

I'm developing in Mac OSX 10.5.5 using Arduino 0011

Thanks

Very Nice, I just updated to version 0013 and your patch worked very well. Thanks You :slight_smile:

Now to finish a door lock using an APSX RW-210 and a boarduino :slight_smile:

Hi David, where those .java files goes?
I want to implement that info because i have some RAM problems with my arduino, it's use LCD, I2C and a 20keys keyboard, i do not why i'm out of memory!!

And i forget....I2C as master!

Thanks!
Frank

Could someone please expound on how to implement these patches? A search of my computer and google has been no help. I'm not sure which files to change.

Thanks,
Matt

It is a little involved. You have to know how to compile java and where to put the compiled files (and how to change the classpath) so they will override the existing classes in the jar files.

Then remember that you did that so when something breaks in the next release you undo any changes before reporting an issue.

Sounds above my pay grade. Too bad though, sounds like a nice feature. I'm working on a project now with ver0013 that showed symptoms of SRAM problems.

All the program is doing is running:
hardware/tools/avr/bin/avr-objdump -h [sketchName].elf

You could run avr-objdump from the command line if you know where to find the elf file for your script (same place as the hex file that gets uploaded).

I ran avr-objdump as per your instructions. Attached is a screen shot of the output. After looking at your code in the first post, I'm thinking .data, .text and .bss are the important lines. I'm also assuming that the "Size" column is in HEX. So .data=542, .text=8380 and .bss=426. The Arduino-0013 compiler outputs a sketch size of 8946 bytes. So I'm thinking .text is part of the sketch size but beyond that I'm not clear on which numbers are included in my sketch size and which are SRAM.

Thanks for your help so far,
Matt

yup, text = flash size, data + bss = the static ram usage (not uncluding any dynamic allocations, call stacks, etc.

You have used up 968 bytes of sram statically, out of 1024 bytes. you might need to introduce your program to PROGMEM.

Copied from J Luciani's post in http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1236113624/11#11, you might find

avr-size YOUR-PROGRAM-NAME.elf

is a bit simpler to just check the headlines.

This gives you a size summary of three elements of memory:

text = code (flash) memory
data = initialised data (in RAM - includes strings etc)
bss = non-initialised data (actually normally set to 0x00) also in RAM.

So total RAM usage = (data + bss), but this does not include dynamic memory allocated from the heap at run time...

All of these values are in DECIMAL. Then the total is given in decimal and hex.

We also must store all initialised data in flash so:

FLASH = text+data
RAM = data + bss

Magnus