From 7c87db3532155c051b902ac83c1feea78eb732ee Mon Sep 17 00:00:00 2001 From: "Loren M. Lang" Date: Mon, 22 Apr 2013 17:48:22 -0700 Subject: [PATCH 1/3] Detect RAM usage and stop if full This resolves issue #1356 and add the ability for the Arduino IDE to detect the amount of RAM allocated to a sketch and compare that to the available RAM on each board. If RAM is more than 90% full, it will fail on building since there is not enough free RAM for the heap and stack to use. --- app/src/processing/app/Sketch.java | 32 ++++++++++---- app/src/processing/app/debug/Sizer.java | 55 ++++++++++++++++++++----- hardware/arduino/avr/boards.txt | 22 ++++++++++ hardware/arduino/avr/platform.txt | 6 ++- 4 files changed, 95 insertions(+), 20 deletions(-) diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index d2ba5e27e..ee93666b8 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -1613,23 +1613,41 @@ public class Sketch { protected void size(PreferencesMap prefs) throws RunnerException { - long size = 0; - String maxsizeString = prefs.get("upload.maximum_size"); - if (maxsizeString == null) + long textSize = 0; + long dataSize = 0; + String maxTextSizeString = prefs.get("upload.maximum_size"); + String maxDataSizeString = prefs.get("upload.maximum_data_size"); + if (maxTextSizeString == null) return; - long maxsize = Integer.parseInt(maxsizeString); + long maxTextSize = Integer.parseInt(maxTextSizeString); + long maxDataSize = -1; + if(maxDataSizeString != null) + maxDataSize = Integer.parseInt(maxDataSizeString); Sizer sizer = new Sizer(prefs); try { - size = sizer.computeSize(); + long[] sizes = sizer.computeSize(); + textSize = sizes[0]; + dataSize = sizes[1]; System.out.println(I18n .format(_("Binary sketch size: {0} bytes (of a {1} byte maximum) - {2}% used"), - size, maxsize, size * 100 / maxsize)); + textSize, maxTextSize, textSize * 100 / maxTextSize)); + if(maxDataSize > 0) { + System.out.println(I18n + .format(_("Memory usage: {0} bytes (of a {1} byte maximum) - {2}% used"), + dataSize, maxDataSize, dataSize * 100 / maxDataSize)); + } else { + System.out.println(I18n + .format(_("Memory usage: {0} bytes"), + dataSize)); + } } catch (RunnerException e) { System.err.println(I18n.format(_("Couldn't determine program size: {0}"), e.getMessage())); } - if (size > maxsize) + /* At least 10% of RAM should be reserved for stack/heap usage */ + if (textSize > maxTextSize || + (maxDataSize > 0 && dataSize > maxDataSize*9/10)) throw new RunnerException( _("Sketch too big; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing it.")); } diff --git a/app/src/processing/app/debug/Sizer.java b/app/src/processing/app/debug/Sizer.java index 89bfe5ee0..67649df68 100644 --- a/app/src/processing/app/debug/Sizer.java +++ b/app/src/processing/app/debug/Sizer.java @@ -33,18 +33,30 @@ import processing.app.helpers.PreferencesMap; import processing.app.helpers.StringReplacer; public class Sizer implements MessageConsumer { - private long size; + private long textSize; + private long dataSize; + private long eepromSize; private RunnerException exception; private PreferencesMap prefs; private String firstLine; - private Pattern pattern; + private Pattern textPattern; + private Pattern dataPattern; + private Pattern eepromPattern; public Sizer(PreferencesMap _prefs) { prefs = _prefs; - pattern = Pattern.compile(prefs.get("recipe.size.regex")); + textPattern = Pattern.compile(prefs.get("recipe.size.regex")); + dataPattern = null; + String pref = prefs.get("recipe.size.regex.data"); + if(pref != null) + dataPattern = Pattern.compile(pref); + eepromPattern = null; + pref = prefs.get("recipe.size.regex.eeprom"); + if(pref != null) + eepromPattern = Pattern.compile(pref); } - public long computeSize() throws RunnerException { + public long[] computeSize() throws RunnerException { int r = 0; try { @@ -52,7 +64,9 @@ public class Sizer implements MessageConsumer { String cmd[] = StringReplacer.formatAndSplit(pattern, prefs, true); exception = null; - size = -1; + textSize = -1; + dataSize = -1; + eepromSize = -1; Process process = Runtime.getRuntime().exec(cmd); MessageSiphon in = new MessageSiphon(process.getInputStream(), this); MessageSiphon err = new MessageSiphon(process.getErrorStream(), this); @@ -77,17 +91,36 @@ public class Sizer implements MessageConsumer { if (exception != null) throw exception; - if (size == -1) + if (textSize == -1) throw new RunnerException(firstLine); - return size; + return new long[] { textSize, dataSize, eepromSize }; } public void message(String s) { if (firstLine == null) firstLine = s; - Matcher matcher = pattern.matcher(s.trim()); - if (matcher.matches()) - size = Long.parseLong(matcher.group(1)); + Matcher textMatcher = textPattern.matcher(s.trim()); + if (textMatcher.matches()) { + if (textSize < 0) + textSize = 0; + textSize += Long.parseLong(textMatcher.group(1)); + } + if(dataPattern != null) { + Matcher dataMatcher = dataPattern.matcher(s.trim()); + if (dataMatcher.matches()) { + if (dataSize < 0) + dataSize = 0; + dataSize += Long.parseLong(dataMatcher.group(1)); + } + } + if(eepromPattern != null) { + Matcher eepromMatcher = eepromPattern.matcher(s.trim()); + if (eepromMatcher.matches()) { + if (eepromSize < 0) + eepromSize = 0; + eepromSize += Long.parseLong(eepromMatcher.group(1)); + } + } } -} \ No newline at end of file +} diff --git a/hardware/arduino/avr/boards.txt b/hardware/arduino/avr/boards.txt index 6627cfa1a..7d740bd26 100644 --- a/hardware/arduino/avr/boards.txt +++ b/hardware/arduino/avr/boards.txt @@ -8,6 +8,7 @@ uno.name=Arduino Uno uno.upload.tool=avrdude uno.upload.protocol=arduino uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 uno.upload.speed=115200 uno.bootloader.tool=avrdude @@ -46,6 +47,7 @@ atmega328diecimila.build.variant=standard atmega328diecimila.menu.cpu.atmega328=ATmega328 atmega328diecimila.menu.cpu.atmega328.upload.maximum_size=30720 +atmega328diecimila.menu.cpu.atmega328.upload.maximum_data_size=2048 atmega328diecimila.menu.cpu.atmega328.upload.speed=57600 atmega328diecimila.menu.cpu.atmega328.bootloader.high_fuses=0xDA @@ -59,6 +61,7 @@ atmega328diecimila.menu.cpu.atmega328.build.mcu=atmega328p atmega328diecimila.menu.cpu.atmega168=ATmega168 atmega328diecimila.menu.cpu.atmega168.upload.maximum_size=14336 +atmega328diecimila.menu.cpu.atmega168.upload.maximum_data_size=1024 atmega328diecimila.menu.cpu.atmega168.upload.speed=19200 atmega328diecimila.menu.cpu.atmega168.bootloader.high_fuses=0xdd @@ -88,6 +91,7 @@ nano.build.variant=eightanaloginputs nano.menu.cpu.atmega328=ATmega328 nano.menu.cpu.atmega328.upload.maximum_size=30720 +nano.menu.cpu.atmega328.upload.maximum_data_size=2048 nano.menu.cpu.atmega328.upload.speed=57600 nano.menu.cpu.atmega328.bootloader.low_fuses=0xFF @@ -102,6 +106,7 @@ menu.cpu.nano.atmega328.build.mcu=atmega328p nano.menu.cpu.atmega168=ATmega168 nano.menu.cpu.atmega168.upload.maximum_size=14336 +nano.menu.cpu.atmega168.upload.maximum_data_size=1024 nano.menu.cpu.atmega168.upload.speed=19200 nano.menu.cpu.atmega168.bootloader.low_fuses=0xff @@ -165,6 +170,7 @@ leonardo.name=Arduino Leonardo leonardo.upload.tool=avrdude leonardo.upload.protocol=avr109 leonardo.upload.maximum_size=28672 +leonardo.upload.maximum_data_size=2560 leonardo.upload.speed=57600 leonardo.upload.disable_flushing=true leonardo.upload.use_1200bps_touch=true @@ -193,6 +199,7 @@ micro.name=Arduino Micro micro.upload.tool=avrdude micro.upload.protocol=avr109 micro.upload.maximum_size=28672 +micro.upload.maximum_data_size=2560 micro.upload.speed=57600 micro.upload.disable_flushing=true micro.upload.use_1200bps_touch=true @@ -221,6 +228,7 @@ esplora.name=Arduino Esplora esplora.upload.tool=avrdude esplora.upload.protocol=avr109 esplora.upload.maximum_size=28672 +esplora.upload.maximum_data_size=2560 esplora.upload.speed=57600 esplora.upload.disable_flushing=true esplora.upload.use_1200bps_touch=true @@ -265,6 +273,7 @@ mini.build.variant=eightanaloginputs mini.menu.cpu.atmega328=ATmega328 mini.menu.cpu.atmega328.upload.maximum_size=28672 +mini.menu.cpu.atmega328.upload.maximum_data_size=2048 mini.menu.cpu.atmega328.upload.speed=115200 mini.menu.cpu.atmega328.bootloader.high_fuses=0xd8 @@ -278,6 +287,7 @@ mini.menu.cpu.atmega328.build.mcu=atmega328p mini.menu.cpu.atmega168=ATmega168 mini.menu.cpu.atmega168.upload.maximum_size=14336 +mini.menu.cpu.atmega168.upload.maximum_data_size=1024 mini.menu.cpu.atmega168.upload.speed=19200 mini.menu.cpu.atmega168.bootloader.high_fuses=0xdd @@ -293,6 +303,7 @@ ethernet.name=Arduino Ethernet ethernet.upload.tool=avrdude ethernet.upload.protocol=arduino ethernet.upload.maximum_size=32256 +ethernet.upload.maximum_data_size=2048 ethernet.upload.speed=115200 ethernet.bootloader.tool=avrdude @@ -316,6 +327,7 @@ fio.name=Arduino Fio fio.upload.tool=avrdude fio.upload.protocol=arduino fio.upload.maximum_size=30720 +fio.upload.maximum_data_size=2048 fio.upload.speed=57600 fio.bootloader.tool=avrdude @@ -355,6 +367,7 @@ bt.build.variant=eightanaloginputs ## ----------------------- bt.menu.cpu.atmega328=ATmega328 bt.menu.cpu.atmega328.upload.maximum_size=28672 +bt.menu.cpu.atmega328.upload.maximum_data_size=2048 bt.menu.cpu.atmega328.bootloader.high_fuses=0xd8 bt.menu.cpu.atmega328.bootloader.extended_fuses=0x05 @@ -366,6 +379,7 @@ bt.menu.cpu.atmega328.build.mcu=atmega328p ## ----------------------- bt.menu.cpu.atmega168=ATmega168 bt.menu.cpu.atmega168.upload.maximum_size=14336 +bt.menu.cpu.atmega168.upload.maximum_data_size=1024 bt.menu.cpu.atmega168.bootloader.high_fuses=0xdd bt.menu.cpu.atmega168.bootloader.extended_fuses=0x00 @@ -380,6 +394,7 @@ LilyPadUSB.name=LilyPad Arduino USB LilyPadUSB.upload.tool=avrdude LilyPadUSB.upload.protocol=avr109 LilyPadUSB.upload.maximum_size=28672 +LilyPadUSB.upload.maximum_data_size=2560 LilyPadUSB.upload.speed=57600 LilyPadUSB.upload.disable_flushing=true LilyPadUSB.upload.use_1200bps_touch=true @@ -423,6 +438,7 @@ lilypad.build.variant=standard lilypad.menu.cpu.atmega328=ATmega328 lilypad.menu.cpu.atmega328.upload.maximum_size=30720 +lilypad.menu.cpu.atmega328.upload.maximum_data_size=2048 lilypad.menu.cpu.atmega328.upload.speed=57600 lilypad.menu.cpu.atmega328.bootloader.low_fuses=0xFF @@ -437,6 +453,7 @@ lilypad.menu.cpu.atmega328.build.mcu=atmega328p lilypad.menu.cpu.atmega168=ATmega168 lilypad.menu.cpu.atmega168.upload.maximum_size=14336 +lilypad.menu.cpu.atmega168.upload.maximum_data_size=1024 lilypad.menu.cpu.atmega168.upload.speed=19200 lilypad.menu.cpu.atmega168.bootloader.low_fuses=0xe2 @@ -466,6 +483,7 @@ pro.build.variant=standard pro.menu.cpu.16MHzatmega328=ATmega328 (5V, 16 MHz) pro.menu.cpu.16MHzatmega328.upload.maximum_size=30720 +pro.menu.cpu.16MHzatmega328.upload.maximum_data_size=2048 pro.menu.cpu.16MHzatmega328.upload.speed=57600 pro.menu.cpu.16MHzatmega328.bootloader.low_fuses=0xFF @@ -481,6 +499,7 @@ pro.menu.cpu.16MHzatmega328.build.f_cpu=16000000L pro.menu.cpu.8MHzatmega328=ATmega328 (3.3V, 8 MHz) pro.menu.cpu.8MHzatmega328.upload.maximum_size=30720 +pro.menu.cpu.8MHzatmega328.upload.maximum_data_size=2048 pro.menu.cpu.8MHzatmega328.upload.speed=57600 pro.menu.cpu.8MHzatmega328.bootloader.low_fuses=0xFF @@ -496,6 +515,7 @@ pro.menu.cpu.8MHzatmega328.build.f_cpu=8000000L pro.menu.cpu.16MHzatmega168=ATmega168 (5V, 16 MHz) pro.menu.cpu.16MHzatmega168.upload.maximum_size=14336 +pro.menu.cpu.16MHzatmega168.upload.maximum_data_size=1024 pro.menu.cpu.16MHzatmega168.upload.speed=19200 pro.menu.cpu.16MHzatmega168.bootloader.low_fuses=0xff @@ -511,6 +531,7 @@ pro.menu.cpu.16MHzatmega168.build.f_cpu=16000000L pro.menu.cpu.8MHzatmega168=ATmega168 (3.3V, 8 MHz) pro.menu.cpu.8MHzatmega168.upload.maximum_size=14336 +pro.menu.cpu.8MHzatmega168.upload.maximum_data_size=1024 pro.menu.cpu.8MHzatmega168.upload.speed=19200 pro.menu.cpu.8MHzatmega168.bootloader.low_fuses=0xc6 @@ -544,6 +565,7 @@ atmegang.build.variant=standard atmegang.menu.cpu.atmega168=ATmega168 atmegang.menu.cpu.atmega168.upload.maximum_size=14336 +atmegang.menu.cpu.atmega168.upload.maximum_data_size=1024 atmegang.menu.cpu.atmega168.bootloader.low_fuses=0xff atmegang.menu.cpu.atmega168.bootloader.high_fuses=0xdd diff --git a/hardware/arduino/avr/platform.txt b/hardware/arduino/avr/platform.txt index 5fc26822f..6bbe078ab 100644 --- a/hardware/arduino/avr/platform.txt +++ b/hardware/arduino/avr/platform.txt @@ -53,8 +53,10 @@ recipe.objcopy.eep.pattern="{compiler.path}{compiler.objcopy.cmd}" {compiler.obj recipe.objcopy.hex.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.hex" ## Compute size -recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.hex" -recipe.size.regex=Total\s+([0-9]+).* +recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf" +recipe.size.regex=^(?:\.text|\.data|\.bootloader)\s+([0-9]+).* +recipe.size.regex.data=^(?:\.data|\.bss|\.noinit)\s+([0-9]+).* +recipe.size.regex.eeprom=^(?:\.eeprom)\s+([0-9]+).* # AVR Uploader/Programmers tools From c35e57ab4ed7c870165f977bbf2ef51ccfc7ff11 Mon Sep 17 00:00:00 2001 From: "Loren M. Lang" Date: Mon, 22 Apr 2013 18:13:31 -0700 Subject: [PATCH 2/3] Don't report memory usage if it's unknown --- app/src/processing/app/Sketch.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index ee93666b8..376d8dbf4 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -1631,14 +1631,16 @@ public class Sketch { System.out.println(I18n .format(_("Binary sketch size: {0} bytes (of a {1} byte maximum) - {2}% used"), textSize, maxTextSize, textSize * 100 / maxTextSize)); - if(maxDataSize > 0) { - System.out.println(I18n - .format(_("Memory usage: {0} bytes (of a {1} byte maximum) - {2}% used"), - dataSize, maxDataSize, dataSize * 100 / maxDataSize)); - } else { - System.out.println(I18n - .format(_("Memory usage: {0} bytes"), - dataSize)); + if(dataSize >= 0) { + if(maxDataSize > 0) { + System.out.println(I18n + .format(_("Memory usage: {0} bytes (of a {1} byte maximum) - {2}% used"), + dataSize, maxDataSize, dataSize * 100 / maxDataSize)); + } else { + System.out.println(I18n + .format(_("Memory usage: {0} bytes"), + dataSize)); + } } } catch (RunnerException e) { System.err.println(I18n.format(_("Couldn't determine program size: {0}"), From 090f7216062e60656ab81fe8992b41341b25554d Mon Sep 17 00:00:00 2001 From: "Loren M. Lang" Date: Thu, 2 May 2013 13:10:02 -0700 Subject: [PATCH 3/3] Clarified error messages and added a configurable warning level Changed memory usage check to only fail build on 100%+ usage and added a configurable warning level for memory usage defaulting to 75%. Clarified error and warning messages related to memory usage to specify that this is the minimum memory usage. --- app/src/processing/app/Sketch.java | 16 +++++++++++----- build/shared/lib/preferences.txt | 2 ++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index 376d8dbf4..ea68fa70e 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -1634,11 +1634,11 @@ public class Sketch { if(dataSize >= 0) { if(maxDataSize > 0) { System.out.println(I18n - .format(_("Memory usage: {0} bytes (of a {1} byte maximum) - {2}% used"), + .format(_("Minimum Memory usage: {0} bytes (of a {1} byte maximum) - {2}% used"), dataSize, maxDataSize, dataSize * 100 / maxDataSize)); } else { System.out.println(I18n - .format(_("Memory usage: {0} bytes"), + .format(_("Minimum Memory usage: {0} bytes"), dataSize)); } } @@ -1647,11 +1647,17 @@ public class Sketch { e.getMessage())); } - /* At least 10% of RAM should be reserved for stack/heap usage */ - if (textSize > maxTextSize || - (maxDataSize > 0 && dataSize > maxDataSize*9/10)) + if (textSize > maxTextSize) throw new RunnerException( _("Sketch too big; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing it.")); + + if (maxDataSize > 0 && dataSize > maxDataSize) + throw new RunnerException( + _("Not enough memory; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing your footprint.")); + + int warnDataPercentage = Integer.parseInt(prefs.get("build.warn_data_percentage")); + if (maxDataSize > 0 && dataSize > maxDataSize*warnDataPercentage/100) + System.out.println(_("Low memory available, stability problems may occur")); } protected String upload(String buildPath, String suggestedClassName, boolean usingProgrammer) diff --git a/build/shared/lib/preferences.txt b/build/shared/lib/preferences.txt index 16290aa0d..2aa9e4202 100644 --- a/build/shared/lib/preferences.txt +++ b/build/shared/lib/preferences.txt @@ -246,6 +246,8 @@ target_package = arduino target_platform = avr board = uno software=ARDUINO +# Warn when data segment uses greater than this percentage +build.warn_data_percentage = 75 programmer = arduino:avrispmkii