Go Down

Topic: Can someone help me troubleshoot my memory issues? (Read 640 times) previous topic - next topic

sholland

Hi,

I'm working on a simple web server, and am experiencing memory issues. Please see my code and output below.

Code: [Select]

#include <SdFat.h>
#include <SdFatUtil.h>
#include <Ethernet.h>
#include <SPI.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 206, 5 };
byte gateway[] = { 192, 168, 206, 1 };
byte subnet[] = { 255, 255, 255, 248 };
Server server(80);
boolean waiting = true;
boolean get = false;
boolean post = false;
boolean boundary = false;

/************ SDCARD STUFF ************/
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;
// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))

void error_P(const char* str) {
 PgmPrint("error: ");
 SerialPrintln_P(str);
 if (card.errorCode()) {
   PgmPrint("SD error: ");
   Serial.print(card.errorCode(), HEX);
   Serial.print(',');
   Serial.println(card.errorData(), HEX);
 }
 while(1);
}

void setup() {
 Serial.begin(9600);
 PgmPrint("Free RAM: ");
 Serial.println(FreeRam());  
 // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
 // breadboards.  use SPI_FULL_SPEED for better performance.
 pinMode(10, OUTPUT);                       // set the SS pin as an output (necessary!)
 digitalWrite(10, HIGH);                    // but turn off the W5100 chip!
 if (!card.init(SPI_HALF_SPEED, 4)) error("card.init failed!");
 // initialize a FAT volume
 if (!volume.init(&card)) error("vol.init failed!");
 PgmPrint("Volume is FAT");
 //Serial.println(volume.fatType(),DEC);
 //Serial.println();
 if (!root.openRoot(&volume)) error("openRoot failed");
 // list file in root with date and size
 PgmPrintln("Files found in root:");
 root.ls(LS_DATE | LS_SIZE);
 Serial.println();
 // Recursive list of all directories
 PgmPrintln("Files found in all dirs:");
 root.ls(LS_R);
 Serial.println();
 PgmPrintln("Done");
 // Debugging complete, we start the server!
 Ethernet.begin(mac, ip);
 server.begin();
}







void loop()
{
     if (waiting) {
           Serial.print("Waiting for connection ...");
           Serial.println(FreeRam());
           waiting = false;
     }
     CheckForConnection();
     post = false;
     get = false;
     boundary = false;
}

void SendFile(String url_args) {
     Serial.print("Request to send file...");
     Serial.println(FreeRam());
     Client client = server.available();
     url_args = url_args.substring(5, url_args.length() -9);
     url_args = url_args.trim();
     if (url_args.length() == 0) { url_args = "index.htm"; };
     Serial.println(url_args);
     if (url_args.length() > 0) {
           if (! file.open(&root, url_args.cstr(), O_READ)) {
                 client.println("HTTP/1.1 404 Not Found");
                 client.println();
           } else {
                 Serial.print("Opening file...");
                 Serial.println(url_args);
                 client.println("HTTP/1.1 200 OK");
                 client.print("Content-Type: ");
                 if (url_args.endsWith(".htm")) {
                       client.println("text/html");
                 } else if (url_args.endsWith(".jpg")) {
                       client.println("image/jpeg");
                 } else if (url_args.endsWith(".ico")) {
                       client.println("image/png");
                 } else if (url_args.endsWith(".png")) {
                       client.println("image/png");
                 } else {
                       client.println("text");
                 }

                 client.println();
                 Serial.print("Sending file... ");
                 int16_t c;
                 while ((c = file.read()) >= 0) {
                       if (client.connected()) {client.print((char)c); continue;}
                       break;
                 }
                 file.close();
                 Serial.println("done");
           }
     } else {
           client.println("HTTP/1.1 404 Not Found");
           client.println();
     }
}


void CheckForConnection() {
     Client client = server.available();
     if (client) {
           while (client.connected()) {
                 String request_line;
                 while (client.available()) {
                       char c = client.read();
                       if (c != '\n' && c != '\r') {
                             request_line += c;
                             continue;
                       }
                       Serial.println(request_line);
                       if ((request_line.startsWith("GET /")) && (request_line.endsWith("HTTP/1.1")) && (!get) && (!post)) {
                             Serial.println("Found GET request");
                             get = true;
                             SendFile(request_line.cstr());
                       } else if ((request_line.startsWith("POST /")) && (request_line.endsWith("HTTP/1.1")) && (!get) && (!post)) {
                             Serial.println("Found POST request");
                             post = true;
                       } else if ((request_line.startsWith("Content-Type:")) && (!get) && (post) && (!boundary)) {
                             Serial.println("Found boundary");
                             boundary = true;
                             int boundary_begin = request_line.indexOf("boundary=");
                             request_line = request_line.substring(boundary_begin);
                             request_line = request_line.trim();
                             Serial.print("Boundary : ");
                             Serial.println(request_line);
                       }
                       request_line = "";
                 }
                 delay(1);
                 client.stop();
                 waiting = true;
           }
     }
     return;
}


This is what the console output looks like when trying to grab index.htm:

Code: [Select]

tail -f /dev/ttyUSB0
Free RAM: 827
Volume is FATFiles found in root:
INDEX.HTM     2010-09-13 15:50:30 1318
FAVICON.ICO   2010-09-13 15:53:12 1406
LOGO.JPG      2010-09-10 02:20:04 8249
CONTACT.HTM   2010-09-10 21:09:44 908
PARTS.JPG     2010-09-10 22:29:20 68907
SETUP1.JPG    2010-09-10 22:29:00 38175
SETUP2.JPG    2010-09-10 22:29:00 45070
Files found in all dirs:
INDEX.HTM
FAVICON.ICO
LOGO.JPG
CONTACT.HTM
PARTS.JPG
SETUP1.JPG
SETUP2.JPG
Done
Waiting for connection ...827
GET / HTTP/1.1
Found GET request
Request to send file...616
index.htm
Opening file...index.htm
Sending file... done

User-Agent: curl/7.19.5 (x86_64-pc-linux-gnu) libcurl/7.19.5 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.15

Host: 192.168.206.5

Accept: */*



Waiting for connection ...584



The numbers at the end of "Waiting for connection ..." and "Request to send file..." are the print out of FreeRam();

From what I can tell it would seem that the String request_line is the problem. Its like its not letting go of memory.

If I let it run for a few hours serving the page and images the memory only gets so low.. but never gets close to where it starts in the main loop, typically around 350. I would think that it should go back up to 827 or close to it if everything free's up properly.

I had read posts about memory issues using Strings in that the string wasn't being free'd. I am using the latest Linux build (arduino-0019). I checked WString.h and it has the destructor:

Code: [Select]

~String() { free(_buffer); _length = _capacity = 0;}


Also note

Code: [Select]

SendFile(request_line.cstr());


I had to modify WString.h with:

Code: [Select]

const char * cstr() const {return _buffer;}


So that I could pass my String object properly to :

Code: [Select]

if (! file.open(&root, url_args.cstr(), O_READ)) {
  ....
}



I'm still learning C/C++ so its not very apparent where I'm going wrong and working with such tight memory restrictions is showing how poor my coding skills really are.

Any help in the right direction would be appreciated.

--
Shawn

PaulS

The line:
Code: [Select]
Client client = server.available();
occurs in SendFile and CheckConnection.
Does this create a new Client instance? Does this client instance ever get deleted?

sholland

I believe the answer is yes, no?

I'm not sure how to delete it. I couldn't find a destructor or a call to free in any of the Ethernet library files.

The only other Object instances I use is String request_line. And that has a destructor. So I'm confused about the Client object. The documentations don't talk about deleting it.

In my case, what is the correct way of creating this object instance and deleting it.

PaulS

Quote
I believe the answer is yes, no?

I believe that you are right. I looked at the Server and Client classes. There is no destructor for the Client class. This means that any instances of the Client class that go out of scope leak memory.

Once could add a destructor to the Client class. It doesn't really need to do anything, except make sure the socket it is using is disconnected (with a call to stop()).

It looks like the Server class think Client instances are free. It creates and abandons them all over the place. It could stand to have a destructor, too.

mellis

The Client doesn't allocate any memory (on the heap), so it doesn't have anything to deallocate in a destructor.  Calling stop() in a destructor would cause problems if you copied the Client, e.g. by passing it by value to a function.

Go Up