Forum: Mikrocontroller und Digitale Elektronik Speicher Leak lwip netconn


von Pascal H. (_pascal_)


Lesenswert?

Hi,

ich habe ein Problem mit meinem lwip. Nachdem ca. 5-6 mal ein request 
verarbeitet wurde, bleibt der controller stehen - ich lande in der _sbrk 
function (Heap <=> Stack Kollision).

In der 'serve' function reserviere ich selbst doch gar keinen speicher. 
Passiert doch alles auf dem Stack. Klar lwip - der netbuf - aber der 
wird ja unten ueber netbuf_delete(inbuf) wieder freigegeben.

Bezueglich Heap Speicher mit free freigeben stellt sich mir allerdings 
die Frage wie das gehen soll. Der HeapPointer wird doch immer weiter 
hoch geschoben. Egal ob free irgendwas davor freigibt, ein weiteres 
reservieren schiebt den HeapPointer immer weiter oder nicht?

1
    #include <HttpServer.h>
2
3
    const static char http_html_hdr[] = "HTTP/1.1 200 OK\r\nContent-type: text/html\r\n\r\n";
4
    const static char http_index_html[] = "<html><head><title>Congrats!</title></head><body><h1>Welcome to our lwIP HTTP server!</h1><p>This is a small test page, served by httpserver-netconn.</body></html>";
5
6
    HttpServer::HttpServer(ip_addr *address, uint16_t port) :
7
                    address(address), port(port) {
8
9
    }
10
11
    void HttpServer::schedule() {
12
13
            char buf[16];
14
            sprintf(taskName, "HttpServer_%s_%d", ipaddr_ntoa_r(address, buf, 16), port);
15
            DEBUGOUT("[TASK] Listen Address: %s Port: %d\r\n", ipaddr_ntoa_r(address, buf, 16), port);
16
17
            this->handle = sys_thread_new(this->taskName, &taskfun, this, 512, DEFAULT_THREAD_PRIO);
18
19
    }
20
21
    void HttpServer::run() {
22
23
            struct netconn *conn, *newconn;
24
            err_t err;
25
26
            try {
27
28
                    /* Create a new TCP connection handle */
29
                    conn = netconn_new(NETCONN_TCP);
30
                    if (conn == NULL) {
31
                            throw InvalidNetConn;
32
                    }
33
34
                    /* Bind to port 80 (HTTP) with default IP address */
35
                    netconn_bind(conn, this->address, this->port);
36
37
                    /* Put the connection into LISTEN state */
38
                    netconn_listen(conn);
39
40
                    do {
41
42
                            err = netconn_accept(conn, &newconn);
43
                            if (err == ERR_OK) {
44
                                    this->serve(newconn);
45
                                    netconn_delete(newconn);
46
                            }
47
48
                    } while (err == ERR_OK);
49
50
                    LWIP_DEBUGF(HTTPD_DEBUG, ("http_server_netconn_thread: netconn_accept received error %d, shutting down", err));
51
52
            } catch (int error) {
53
54
                    switch (error) {
55
                    case InvalidNetConn:
56
                            LWIP_DEBUGF(HTTPD_DEBUG, ("http_server_netconn_thread: Invalid NetConn, shutting down"));
57
                            break;
58
                    }
59
60
            }
61
62
            netconn_close(conn);
63
            netconn_delete(conn);
64
65
    }
66
67
    void HttpServer::serve(struct netconn *conn) {
68
69
            struct netbuf *inbuf;
70
            char *buf, *tbuf;
71
            u16_t buflen;
72
            err_t err;
73
74
            /* Read the data from the port, blocking if nothing yet there.
75
            We assume the request (the part we care about) is in one netbuf */
76
            err = netconn_recv(conn, &inbuf);
77
78
            if (err != ERR_OK) {
79
                    return;
80
            }
81
82
            try {
83
84
                    netbuf_data(inbuf, (void**) &buf, &buflen);
85
86
                    netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);
87
                    netconn_write(conn, http_index_html, sizeof(http_index_html) - 1, NETCONN_NOCOPY);
88
89
                    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("HTTPD: Got URI %s\r\n", buf));
90
91
            } catch (int error) {
92
93
                    switch (error) {
94
                    case InvalidRequest:
95
                            LWIP_DEBUGF(HTTPD_DEBUG, ("HTTPD: Invalid Request Line\r\n"));
96
                            break;
97
                    }
98
99
            }
100
101
            /* Close the connection (server closes in HTTP) */
102
            netconn_close(conn);
103
104
            /* Delete the buffer (netconn_recv gives us ownership,
105
            so we have to make sure to deallocate the buffer) */
106
            netbuf_delete(inbuf);
107
108
    }
109
110
    HttpServer::~HttpServer() {
111
            // TODO Auto-generated destructor stub
112
    }
113
114
    caddr_t _sbrk(int incr) {
115
116
            if (heap_ptr + incr < (void*) __get_MSP()) {
117
118
                    caddr_t base = heap_ptr;
119
                    heap_ptr += incr;
120
121
                    return base;
122
123
            } else {
124
125
                    errno = ENOMEM;
126
                    return (caddr_t) -1;
127
128
            }
129
130
    }

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das Argument von sbrk ist vorzeichenbehaftet.

von Pascal H. (_pascal_)


Lesenswert?

Also wenn ich _sbrk mit einem breakpoint versehe, ist incr immer 
positiv, nie negativ.

Ist folgendes Beispiel falsch?
Der HeapPointer wird immer weiter geschoben, was passiert mit dem 
freigegebenen Speicher von ptr_m? Kuemmert sich newlib nano darum diesen 
Speicher wieder zu verwenden? Bei mir scheint das nicht der fall zu 
sein.

                             HeapPointer
HeapStart                    0x10000000
malloc->_sbrk(1000) ptr_m    0x10001000
malloc->_sbrk(1000) ptr_n    0x10002000
free(ptr_m)                  0x10002000
malloc->_sbrk(1000) ptr_k    0x10003000

: Bearbeitet durch User
von Dieter Graef (Gast)


Lesenswert?

Pascal H. schrieb:
> ich lande in der _sbrk
> function (Heap <=> Stack Kollision).

Dreh mal and der Optionen:
#define MEM_SIZE
#define EMAC_RECIVE_THREAD_STACKSIZE
#define DEFAULT_THREAD_STACKSIZE
#define TCPIP_THREAD_STACKSIZE
in der lwipopts.h

m.f.G.
Dieter

von Pascal H. (_pascal_)


Lesenswert?

In welche Richtung?

Wenn ich MEM_SIZE erhoehe laeuft RTOS gar nicht los.

1
#define configMINIMAL_STACK_SIZE  ( ( unsigned short ) 128 )
2
#define MEM_SIZE                        (12 * 1024)
3
4
#define DEFAULT_THREAD_STACKSIZE        (configMINIMAL_STACK_SIZE)
5
#define TCPIP_THREAD_STACKSIZE          (configMINIMAL_STACK_SIZE + 256)

von Dieter Graef (Gast)


Lesenswert?

Pascal H. schrieb:
> In welche Richtung?

Hängt von der Hardware ab. Ich hab STM32F7 und aase ein wenig der 
Stabilität zu liebe.
#define DEFAULT_THREAD_STACKSIZE    512
#define TCPIP_THREAD_STACKSIZE      1536
#define MEM_SIZE                        (24*1024)

#define PBUF_POOL_SIZE                  20
#define PBUF_POOL_BUFSIZE               1524

#define MEMP_NUM_PBUF                   50
#define MEMP_NUM_UDP_PCB                10
#define MEMP_NUM_TCP_PCB                20
#define MEMP_NUM_TCP_PCB_LISTEN         20

Ich musste fürs debuggen erhöhen da kommen ja noch Printaufrufe dazu.
Das gibt so 100 Kilo Ram aber ich hab ja 320.Und da ist auch noch mein 
Webserver mit drin der 10 Threads für sich braucht.

von Pascal H. (_pascal_)


Lesenswert?

Ich hab einen lpc1768 mit 32k RAM.

Diese Function wird bei jedem request aufgerufen. Ich gebe da den 
heapPointer aus. Komischerweise schiebt sich der nicht immer weiter aber 
nach 5-6 request erhoeht er sich immer.

Und nach ca 20 request gibt meine sbrk 0 zurueck da ich bei __get_MSP() 
angelangt bin.

Ich verstehe nicht warum immer mehr heap reserviert wird.

Auch wenn ich nicht sprintf verwende und als body
1
const static char http_index_html[] = "<html><head><title>lwip netconn</title></head><body><h1>Heap Pointer: </h1></body></html>";
2
3
...
4
5
netconn_write(conn, http_index_html, sizeof(http_index_html) - 1, NETCONN_NOCOPY);

verwende passiert das gleiche, der controller steht.
1
void HttpServer::serve(struct netconn *conn) {
2
3
  struct netbuf *inbuf;
4
  char *buf, *tbuf;
5
  u16_t buflen;
6
  err_t err;
7
8
  /* Read the data from the port, blocking if nothing yet there.
9
   We assume the request (the part we care about) is in one netbuf */
10
  err = netconn_recv(conn, &inbuf);
11
12
  if (err != ERR_OK) {
13
    return;
14
  }
15
16
  try {
17
18
    netbuf_data(inbuf, (void**) &buf, &buflen);
19
20
    char buffer[98];
21
    sprintf(buffer, "<html><head><title>lwip netconn</title></head><body><h1>Heap Pointer: %x</h1></body></html>\0", getHeapPointer());
22
23
    netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);
24
    netconn_write(conn, buffer, sizeof(buffer) - 1, NETCONN_COPY);
25
26
    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("HTTPD: Got URI %s\r\n", buf));
27
28
  } catch (int error) {
29
30
    switch (error) {
31
    case InvalidRequest:
32
      LWIP_DEBUGF(HTTPD_DEBUG, ("HTTPD: Invalid Request Line\r\n"));
33
      break;
34
    }
35
36
  }
37
38
  /* Close the connection (server closes in HTTP) */
39
  netconn_close(conn);
40
41
  /* Delete the buffer (netconn_recv gives us ownership,
42
   so we have to make sure to deallocate the buffer) */
43
  netbuf_delete(inbuf);
44
45
}

: Bearbeitet durch User
von Dieter Graef (Gast)


Lesenswert?

Pascal H. schrieb:
> Ich hab einen lpc1768 mit 32k RAM

Da kannst du die lwip Optionen bei mbedmicro auf Github schaun.
https://github.com/mbedmicro/mbed/blob/master/libraries/net/lwip/lwip/lwipopts.h

Ich glaub deren Trick sind winzige Pakete.

m.f.G.
Dieter

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.