Pergunta

Eu encontrei um problema em um programa C em execução em um microcontrolador AVR (ATmega328P). Eu acredito que é devido a uma colisão pilha / pilha, mas eu gostaria de ser capaz de confirmar isso.

Existe alguma maneira que eu possa visualizar o uso SRAM pela pilha ea pilha?

Nota:. o programa é compilado com avr-gcc e usos avr-libc

Update: O real problema que estou tendo é que a implementação malloc está a falhar (retornando NULL). Todos mallocing acontece na inicialização e todos freeing acontece no final da aplicação (que na prática nunca é desde a parte principal do aplicativo está em um loop infinito). Então, eu tenho certeza que a fragmentação não é o problema.

Foi útil?

Solução

Você diz malloc está falhando e retornando NULL:

A causa óbvia que você deve olhar em primeiro lugar é que a sua pilha é "cheio" - ou seja, a memória que você pediu para malloc não podem ser alocados, porque ele não está disponível.

Há dois cenários a ter em conta:

R: Você tem uma pilha de 16 K, você já malloced 10 K e você tentar e malloc mais um 10K. Sua pilha é simplesmente muito pequeno.

b: Mais comumente, você tem um k Heap 16, você está fazendo um monte de malloc / chamadas / realloc livres e sua pilha é inferior a 50% 'cheio': Você chama malloc para 1K e ele falhar - e aí? Resposta - o espaço de pilha livre é fragmentada - não há uma contigous 1K de memória livre que pode ser retornado. gerentes C Heap não pode compactar a pilha quando isso acontecer, então você está geralmente em um mau caminho. Existem técnicas para fragmentação evitar, mas é difícil saber se este é realmente o problema. Você precisa adicionar calços de registro para malloc e livre de modo que você pode ter uma idéia do que operações de memória dinâmica está sendo executada.

EDIT:

Você diz que todos os mallocs acontecer na inicialização, de modo a fragmentação não é o problema.

Nesse caso, ele deve ser fácil de substituir a alocação dinâmica com estático.

exemplo de código antigo:

char *buffer;

void init()
{
  buffer = malloc(BUFFSIZE);
}

novo código:

char buffer[BUFFSIZE];

Depois de ter feito isso em todos os lugares, o seu LINKER deve avisá-lo se tudo não pode caber na memória disponível. Não se esqueça de reduzir o tamanho da pilha -. Mas cuidado que alguns runtime io funções do sistema ainda pode usar a pilha, então você pode não ser capaz de removê-lo totalmente

Outras dicas

Você pode verificar a utilização estática RAM usando o utilitário avr-size, como descrito no
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=62968 ,
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=82536 ,
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=95638 ,
e http://letsmakerobots.com/node/27115

avr-size -C -x Filename.elf

(documentação AVR-size: http://ccrma.stanford.edu/planetccrma /man/man1/avr-size.1.html )

Segue um exemplo de como configurar isso em um IDE: No Code :: Blocks, Projeto -> Opções de Construção -> Pre / etapas de pós build -> etapas pós-compilação, incluem:

avr-size -C $(TARGET_OUTPUT_FILE) ou
avr-size -C --mcu=atmega328p $(TARGET_OUTPUT_FILE)

Exemplo de saída na extremidade de construção:

AVR Memory Usage
----------------
Device: atmega16

Program:    7376 bytes (45.0% Full)
(.text + .data + .bootloader)

Data:         81 bytes (7.9% Full)
(.data + .bss + .noinit)

EEPROM:       63 bytes (12.3% Full)
(.eeprom) 

Os dados é o seu uso SRAM, e é apenas a quantidade que o compilador sabe em tempo de compilação. Você também precisa de espaço para as coisas criadas na runtime (uso particular pilha).

Para verificar o uso de pilha (dynamic RAM), de http://jeelabs.org/2011/05/22/atmega-memory-use/

Aqui está uma pequena função de utilidade que determina quanta memória RAM é actualmente não utilizado:

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

E aqui está um esboço usando esse código:

void setup () {
    Serial.begin(57600);
    Serial.println("\n[memCheck]");
    Serial.println(freeRam());
}

O FreeRAM () devolve o número de bytes existe entre o final da pilha ea última memória alocada na pilha, por isso, é efetivamente o quanto a pilha / pilha pode crescer antes que eles colidem.

Você pode verificar o retorno desta função em torno do código que você suspeitar que pode estar causando pilha / colisão heap.

A abordagem usual seria para preencher a memória com um padrão conhecido e, em seguida, para verificar quais áreas são substituídos.

Se você estiver usando tanto pilha e pilha, então ele pode ser um pouco mais complicado. Vou explicar o que eu fiz quando nenhum heap é usado. Como regra geral, todas as empresas que eu trabalhei para (no domínio do software C incorporado) ter evitado usando pilha para pequenos projetos-de embutidos evitar a incerteza de disponibilidade de memória heap. Nós usamos variáveis ??estaticamente declarados vez.

Um método consiste em preencher a maior parte da área da pilha com um padrão conhecido (por exemplo, 0x55) no arranque. Isso geralmente é feito por um pequeno pedaço de código no início da execução de software, seja logo no início de main (), ou talvez mesmo antes de main () começa, no código start-up. Tome cuidado para não substituir a pequena quantidade de pilha em uso naquele ponto do curso. Em seguida, após a execução do software por um tempo, inspecionar o conteúdo do espaço de pilha e ver onde o 0x55 ainda está intacto. Como você "inspecionar" depende do seu hardware alvo. Supondo que você tenha um depurador conectado, então você pode simplesmente parar o micro corrida e ler a memória.

Se você tem um depurador que pode fazer um ponto de interrupção memória de acesso (um pouco mais chique do que o ponto de interrupção execução usual), então você pode definir um ponto de interrupção em um local, tais como o limite mais distante do seu espaço de pilha específica pilha. Isso pode ser extremamente útil, pois ele também mostra exatamente o que pouco de código está sendo executado quando se chega a esse grau de uso da pilha. Mas isso requer o depurador para apoiar o recurso de memória de acesso ponto de interrupção e que muitas vezes não é encontrado nos depuradores "low-end".

Se você também estiver usando pilha, então ele pode ser um pouco mais complicado, porque pode ser impossível prever onde pilha e pilha irá colidir.

Não use a alocação de pilha / dinâmico em alvos embutidos. Especialmente com um processador com tais recursos limitados. Em vez redesenhar sua aplicação, porque o problema vai reaparecer como seu programa cresce.

Assumindo que você está usando apenas uma pilha (de modo não um RTOS ou qualquer coisa) e que a pilha está no final da memória, crescendo para baixo, enquanto a pilha está começando depois a região de BSS / DATA, crescendo. Eu vi implementações de malloc que realmente verificar a stackpointer e falhar em uma colisão. Você poderia tentar fazer isso.

Se você não é capaz de adaptar o código malloc, você pode optar por colocar seu stack no início da memória (usando o arquivo linker). Em geral, é sempre uma boa idéia para saber / definir o tamanho máximo da pilha. Se você colocá-lo no início, você obterá um erro na leitura além do início da RAM. A Heap será no final e provavelmente não pode crescer além do fim se é um implemantation decente (retornará NULL em vez disso). Boa coisa é que você sabe que tem 2 casos de erro separadas para 2 questões separadas.

Para descobrir o tamanho máximo da pilha, você poderia encher sua memória com um padrão, executar o aplicativo e ver até onde ele passou, ver também a resposta de Craig.

Se você pode editar o código para o seu montão, você poderia preenchê-lo com um par de bytes extras (complicado em tais baixos recursos) em cada bloco de memória. Estes bytes pode conter um padrão diferente conhecido a partir da pilha. Isso pode dar uma pista, se choca com a pilha por vê-lo aparecer dentro da pilha ou vice-versa.

No Unix como sistemas operacionais uma função de biblioteca sbrk chamado () com um parâmetro de 0 permite que você acesse o endereço mais alto da memória heap alocada dinamicamente. O valor de retorno é um ponteiro void * e pode ser comparado com o endereço de uma pilha variável alocada arbitrária.

Usando o resultado desta comparação devem ser usados ??com cuidado. Dependendo da arquitetura da CPU e do sistema, a pilha pode estar crescendo para baixo a partir de um endereço de alta arbitrária enquanto o alocado pilha irá mover para cima a partir da memória de baixo-bound.

Às vezes o sistema operacional tem outros conceitos para a gestão de memória (ou seja, OS / 9) que lugares heap e pilha em diferentes segmentos de memória em memória livre. Nesses sistemas operacionais - especialmente para sistemas embarcados - você precisa definir os requisitos de memória máxima do seu aplicações de antecedência para permitir que o sistema para alocar segmentos de memória de tamanhos correspondentes.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top