find dead code in code compiled in amtel studio

Go To Last Post
41 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

trying to clean up my code a bit.

anyone know if there is any easy way in atmel studio to find "dead code"?

what I mean with dead code is for example dead function declarations :

.

.

.

void something used();

void something used();

void somethingnotused();

void something used();

void something used();

.

.

.

 

 

or other other simular things. that is unnecessary code that makes the code harder to read and adds pointless complexity.

any one know if there is any easy way to find these or other simular things in atmel studio?

Atmel studio don't give a warning for such things I have noticed. 

Last Edited: Tue. May 14, 2019 - 10:56 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Go to your main routine...check which functions are called...dig a bit in your code...you will find the unnecessary functions.

 

its a bit manual work...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Just switch on -ffunction-sections and -gc-sections (which are on by default in AS7 projects) and all dead code will be garbage collected during the link anyway (and the map file will list what was discarded)

Last Edited: Tue. May 14, 2019 - 01:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Just switch on -ffunction-sections and -gc-sections (which are on by default in AS7 projects) and all dead code will be garbage collected during the link anyway (and the map file will list what was discarded)

 

I didnt know such thing existed in AS7, Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Moe123 wrote:

Go to your main routine...check which functions are called...dig a bit in your code...you will find the unnecessary functions.

 

its a bit manual work...

yes, it just seems like something that a program can do faster and better than a person 

Last Edited: Tue. May 14, 2019 - 01:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Just switch on -ffunction-sections and -gc-sections (which are on by default in AS7 projects) and all dead code will be garbage collected during the link anyway (and the map file will list what was discarded)

 

ok, great. I will check it out. thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The way function sections / gc sections works is that every function is built into separated named flash sections. The linker then only includes in the link any section with a reference counter of 1 or more. Any with 0 reference are garbage collected.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Some dead code is dead because it can never be logically executed & can be tough to spot

 

somewhere in the program:   if Motor A is running faster then 500 RPM, call calibrate valveb

 

valveb cal:  If motor is running faster then 300 rpm skip cal

 

 This may not be such a solid example, but gives the flavor.

 

 

 

 

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avrcandies wrote:
Some dead code is dead because it can never be logically executed & can be tough to spot
Some static analyzers can detect that by data flow analysis akin to detection of inadvertent infinite loops (control flow analysis)

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Some static analyzers can detect that by data flow analysis 

I wonder how advanced it gets...needs to be like a good chess program to analyze all of the paths & probably include advanced logic minimization.

I gave students a non-trivial circuit full of gates to build...later showed them the equivalent circuit was two pieces of wire.

 

 

 

 

 

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Some years back I was a place that used Klocwork and it can really analyze your code. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Cheers!, Thanks for this Appnote.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Just switch on -ffunction-sections and -gc-sections (which are on by default in AS7 projects) and all dead code will be garbage collected during the link anyway (and the map file will list what was discarded)

 

I looked at the map file. But It is really hard to understand the file...

I even tried to add some junk on purpose in the code to se if that popped up in the map file. 

For example adding: int trash=1; and the never using that variable for anything)

But I could never find the purposely added junk in the map file (when I opened it as txt document).

 

Anyone know how to read these files? Are there maybe some manual for this kind of files somewhere? More specifically to find "dead code" like unused stuff etc. ?

Last Edited: Mon. May 27, 2019 - 05:51 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

gchapman wrote:

-Wunused-value

sorry for the complete newbie question here.. But I have not been using these before:

 

where in atmel studio 7 do one add these commands (or what they are called. -Wunused-value) and how do you do to run them?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There's a text box under Miscellaneous where you can add arbitrary extra parameters to be added to the command line.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So I should write it here?..or?.. and then just build the project?

But I see no diffrences when I compile when doing this even thou I know that there are unused variables in the code. 

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But do those unused variables make it into the compiled code? Or are they only in the source code?

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

good question.. I don´t know. I assume atmel studio do not add them to the compiled code after I press "build solution" since they are not used anyway..

I don´t know.. for me it is strange that atmel studio sometimes complains about unused variabels and sometimes not. 

 

I mean it will not be a problem for the program either way I guess.  I just feel like it would help with making the code easier to read, and maybe more stable if you have less leftover forgotten varabels that are never used and sutch things.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are two different situations:

 

1.  dead code elimination

2.  linker removes unused functions

 

(1) can be determined by the Compiler e.g.

static bool flag = false;
    ...
    if (flag) {
        ...
    }
...

(2) the linker examines all the functions, symbols etc from the compilation units e.g. .C, .S, .CPP files and links with .A pre-compiled object libraries

 

Linkers have always rejected unused objects in a library.

Nowadays,  linkers reject unused sections from objects generated by the compilation units

 

Compilers have always detected unused variables, dead code etc

 

But only the Linker knows what goes into the final executable.

The Linker has never reported "unused" objects from a library.

I am not sure how to ask the Linker to report "unused sections" from the compilation units.

 

Think about it.   A C++ class contains a full set of methods.    But a Serial.print("Hello World");  does not use every method from the Serial, Print or Stream class.

 

If the Linker put unused methods into the executable,   it would make life very difficult to fit in a microcontroller.

 

Personally,   I would just trust the Tools.    You only need to worry when you start running out of Flash.    (Or have a size-limited evaluation compiler)

Even then,   you don't need to know "unused sections".   You look at your "used" sections especially greedy code.    e.g. Arduino code that uses String class and/or f-p maths.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why would you write code that you don't intend to use into the project in the first place? If it's just prototyping or debug then after you commit a copy to revision control (in case) then delete it or at least #if 0 it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Brian Fairchild wrote:
But do those unused variables make it into the compiled code?

Easy enough to find out, look at the map file!

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I take your point when it comes to application code.  There is little point in writing functions that you don't use.

 

Re-usable code will probably have several features that you don't necessarily use.    You can just let the Linker discard it.

 

Microprocessor Linkers in the 1980s or AVR linkers in the early 2000s would link "anything" and "everything" in a compilation unit.   So an author would have to remove stuff by hand.

 

The real difference comes with C++ classes and inheritance.    You want to create standardised classes but are well aware that only a small subset of methods are used in any application.

It is better for the Linker to analyse the call graph than to rely on a human author.

 

I deliberately include unused static arrays.   Knowing that the Linker will remove them.

Likewise,    I deliberately include static Boolean.   I know that the Compiler will reject dead code blocks.

This makes neater code than extra pre-processor conditionals.

 

I would get a nasty shock if we went back to a 2005 linker.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Why would you write code that you don't intend to use into the project in the first place? If it's just prototyping or debug then after you commit a copy to revision control (in case) then delete it or at least #if 0 it.

 

I am not quite sure I follow you here.. commit to revision control?

 

The reason is quite simple: error on my part.

if I am writing a somewhat bigger program (like I am now) it is easy to miss to remove for example some variables I ended up not using or not used function declarations for example or stuff like this. And the program does not always warn for junk in the code, so it is easy to miss the junk when you have a big and complex program.

 

I find that it is surprisingly often I find variables that I never ended up using that the Atmel studio does not give a warning for.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:

Brian Fairchild wrote:
But do those unused variables make it into the compiled code?

Easy enough to find out, look at the map file!

 

I did. As I mentioned before I even deliberately added a trash variable to the program to see if I could find the variable in the map file (to find the section for discarded variables in the file).

But searching in the map file for the variable name I could not find anything. But maybe I am doing it wrong? I do not have experience with map files from before and they are quite confusing. how does one see what in the file is discarded by the compiler?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
Personally,   I would just trust the Tools.    You only need to worry when you start running out of Flash

you a probably right. It just feels like there should be an automatic way to make the code more manageable and readable and cutting away the "dead weight". I am surprised the program does not do a better job in finding and warning about this "dead code". 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mannen wrote:
I am surprised the program does not do a better job in finding and warning about this "dead code".
Cohesiveness is a characteristic of computer software.

A compiler compiles, a linker links, a linter lints.

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mannen wrote:

I find that it is surprisingly often I find variables that I never ended up using that the Atmel studio does not give a warning for.

If you enable the warning options

-Wall -Wextra

you will get plenty of warnings for unused variables.

(unless they are 'global' variables ie. variables with external linkage ie. variables defined outside a function without static eg.

static int x;

x can only be used within this c file so the compiler can warn if x is uused

but

int x;

it can't since it might be used by some other c file).

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am not sure if mannen understands "dead code removal"

 

That is what I was trying to show in #21.

 

The C compiler can only determine "unused path" or "unused variable" when it knows the full scope.

 

So a global variable or function might be accessed by another compilation unit.

But a local variable or block only has local scope.   The compiler can analyse the call graph and determine whether it is "dead code".

The Compiler can only report issues when it has 100% analysis e.g. confined to a local block.

 

I agree entirely.    It would be useful to see what code and what variables are discarded by the Linker.

 

In practice,   concentrate on your program logic.    Let the Tools perform their magic.    Only worry if you start running out of Flash.

Then readers can offer a few tips.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mannen wrote:

I am not quite sure I follow you here.. commit to revision control?

Yeah, if you are adding some code just to "try something out" as an experiment then when you have done that you can just push the change into SVN or Git and, having done that, knowing that you have a copy of the code safely stored in case you need to come back to it, you can then delete it from your active files. No point leaving "old rubbish" in your sources.

 

Either that, or, as I say, if you add something experimental then after you are happy it works OK then build it out with "#if 0" or "#ifndef BUILD_EXPERIMENT" or whatever.

 

Otherwise, how does "unused code" get into the project in the first place?

 

Anyway, because AS7 defaults to -ffunction-sections, -fdata-sections and -gc-sections then why do you need to worry? (sometimes annoyingly!) those very effectively remove "dead code" (and variables) anyway.

gchapman wrote:
a linter lints.

I was going to suggest that too. While those linker Garbage Collect options do a good job of removing what is dead (and reporting it in the .map) if you want a true analysis of all the "dead bits" then I would have thought a linter would be the obvious choice. Static code analysers are written with exactly this kind of task in mind. Maybe try one of the "free" ones like cppcheck or splint and if you like the way they work then consider a trade up to one of the professional ones like Gimpel's lint, QAC, Klockwork, etc 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

GNU generally has switches for everything. You just have to wade through the docs.
.
I bet that the Linker has a switch to report discarded symbols and sections from the .O files. No one wants to see unlinked objects from .A library archive
.
David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
I bet that the Linker has a switch to report discarded symbols and sections
It does that by default...

#include <avr/io.h>

char text[20] = "Hello";
int data[14];

void foo(void) {
	PORTB = 0x55;
}

void bar(void) {
	if (PINB & 1) {
		PORTB ^= 0xA5;
	}
}

int main(void)
{
	while(1)
	{
	}
}
Discarded input sections

 .data          0x00000000        0x0 C:/Program Files (x86)/Atmel/Studio/7.0/Packs/Atmel/ATmega_DFP/1.3.300/gcc/dev/atmega328p/avr5/crtatmega328p.o
 .bss           0x00000000        0x0 C:/Program Files (x86)/Atmel/Studio/7.0/Packs/Atmel/ATmega_DFP/1.3.300/gcc/dev/atmega328p/avr5/crtatmega328p.o
 .text          0x00000000        0x0 main.o
 .data          0x00000000        0x0 main.o
 .bss           0x00000000        0x0 main.o
 .text.foo      0x00000000        0x6 main.o
 .text.bar      0x00000000        0xe main.o
 .data.text     0x00000000       0x14 main.o
 COMMON         0x00000000       0x1c main.o
etc.

That tell us that the 6 bytes that constitute "foo()" and 14 bytes for "bar()"were discarded. Also the 20 bytes of text[] were discarded. Because the 28 bytes of data[] are in .common (BSS) then I agree it could possibly have done a better job of annotating that as .data.data rather than "COMMON"!

 

PS by the way both with the GC stuff switched on or off I think I've tried every permutation of -Wunused-value or the more powerful -Wunused and I cannot seem to get ANY output from it - so the suggestion to use that may have been a bit of a red herring!

Last Edited: Wed. May 29, 2019 - 11:47 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Woo-Hoo.   That all goes to show how shallow are my investigative powers.

 

I just looked at a recent MAP file.   Searched for "discarded".   And was suitably impressed by how cleverly the Linker has discarded individual class methods from a C++ project.

 

I suppose that I would have looked harder if I had a specific project that "did not fit" the target Flash.

 

Whereas discarded sections are easy to spot,   analysing which functions are greedy and how to reduce the Flash use is fiddly.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tried this simplistic test in C++ rather than C:

#include <avr/io.h>

class Foo
{
public:
	void foo() {
		PORTB = 0xFF;
	}

	void bar() {
		PORTB = 0x00;
	}
	
	void boom() {
		PORTB = 0x55;
	}
};

int main(void) {
	Foo A;

	while(1) {
		A.boom();
	}
}

My hope was that foo() and bar() might be discarded. It seems from the LSS that they were but the only evidence of this I can find in the .map file is:

Discarded input sections

 .data          0x00000000        0x0 C:/Program Files (x86)/Atmel/Studio/7.0/Packs/Atmel/ATmega_DFP/1.3.300/gcc/dev/atmega2560/avr6/crtatmega2560.o
 .bss           0x00000000        0x0 C:/Program Files (x86)/Atmel/Studio/7.0/Packs/Atmel/ATmega_DFP/1.3.300/gcc/dev/atmega2560/avr6/crtatmega2560.o
 .group         0x00000000        0x8 main.o
 .text          0x00000000        0x0 main.o
 .data          0x00000000        0x0 main.o
 .bss           0x00000000        0x0 main.o
etc.

I take it that the 8 bytes discarded from ".group" are the two functions. To prove that if I use foo() and well as boom() then the LSS shows code for both foo() and boom() but the discard list changes to:

Discarded input sections

 .data          0x00000000        0x0 C:/Program Files (x86)/Atmel/Studio/7.0/Packs/Atmel/ATmega_DFP/1.3.300/gcc/dev/atmega2560/avr6/crtatmega2560.o
 .bss           0x00000000        0x0 C:/Program Files (x86)/Atmel/Studio/7.0/Packs/Atmel/ATmega_DFP/1.3.300/gcc/dev/atmega2560/avr6/crtatmega2560.o
 .group         0x00000000        0x8 main.o
 .group         0x00000000        0x8 main.o
 .text          0x00000000        0x0 main.o
 .data          0x00000000        0x0 main.o
 .bss           0x00000000        0x0 main.o

which is a very curious result as this is now showing two lots of 8 bytes saved from ".group" and yet there is actually more code. 

 

There does not seem to be anything to show which member functions are included/discarded in the .map report. 

 

I'm guessing there probably is some diagnostic one could pass to the linker to tell it to be more verbose about the garbage collection.

 

But other than that it seems C++ is much more difficult to track what is discarded than C.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


I had a read through the avr-ld manual and found this interesting option:

 

 

So this is a possibly more cosmetic way to see the sections discarded than trying to analyze the .MAP file. However it still describes BSS entries simply as "COMMON" so hides their identity. Also this option did not (once again) seem to be of too much use in working out which member functions in a C++ class are discarded. My search for that continues...

 

BTW the above are apparently "4 Errors" as far as AS7 is concerned simply because they are printed to stderr not stdout so the IDE has mistakenly assumed they must be errors. However at the same time it said:

Build succeeded.
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========

So don't necessarily believe everything listed as "Error" ;-)

 

(I wonder if the C++ member function discard is something done in avr-g++ at compile time and not a "last minute thing" by the linker like gc-sections is ?)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff,

I think that the Coompiler has already determined that you have a single object project.
The Compiler can discard foo() and bar() i.e. regular dead code analysis.
.
I was lookinng at classes defined in separate compilation units.
.
No, I have not tried. But I suspect that separate main.cpp, Foo.h and Foo.cpp would pass the whole set of class methods to the Linker.
The Linker does the call graph analysis
.
David.

Last Edited: Wed. May 29, 2019 - 12:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
(I wonder if the C++ member function discard is something done in avr-g++ at compile time and not a "last minute thing" by the linker like gc-sections is ?)

Answering my own question - C++ members are discarded even when -ffunction-sections, -fdata-sections, -gc-sections are not used so clearly this is part of the C++ compiler operation (not the linker). It even happens with -O0.

 

Google suggests that C++11 onwards does something like this by default I believe (and AS7 defaults C++ projects to -std=c++11)

 

EDIT: actually it happens even without -std=c++11 so I think this is even more fundamental to the C++ compiler. It always seems to discard member functions that are not invoked.

 

The bottom line of all this seems to be "just let the tools get on with the job and they will handle things well" whether you talk about C or C++ (but with C you do need gc-sections etc)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes,  I suppose that C++ must always show some intelligence.

 

The default behaviour of a C compiler is to use a single .text section.

Specifying gc-sections make the Linker's life easier.

 

However a Linker could discard blocks from within a section if the object file format can describe ALL references correctly.   For example,   assemblers often optimise branch instructions with the object file of a single compilation unit.

 

Of course you can easily devise ways to fool access permissions or produce self-modifying code.    I suspect that you exploited every trick in your Z80 days.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


mannen wrote:

...I even deliberately added a trash variable to the program to see if I could find the variable in the map file...But searching in the map file for the variable name I could not find anything.

 

Surely that simply means that the compiler/linker has noticed that you don't use that variable and removed it from the final output.

 

Consider this very simple example...

 

#include <mega328p.h>
#include <stdint.h>

uint16_t used_variable;
uint16_t unused_variable;

void used_function (void)
{
	used_variable = 0x55aa;
}

void unused_function (void)
{
	used_variable = 0xaa55;
}

void main(void)
{
	used_function();
}

 

If I look at the .map file my compiler produces I see this...

 

Quote:

RAM Allocation [bytes]

Variable                                                                          Address   Size

------------------------------------------------------------------------------------------------

used_variable                                                                     0x0300       2

EEPROM Allocation [bytes]

Variable                                                                          Address   Size

------------------------------------------------------------------------------------------------

Register Allocation

Variable                                                                          Register  Size

------------------------------------------------------------------------------------------------

FLASH Allocation [words]

Function                                                                          Address   Size

------------------------------------------------------------------------------------------------

used_function                                                                     0x00051      7

main                                                                              0x00058      2

 

Not a mention of the unused variable or the unused function.

 

The compiler/linker does tell me what it has done...

 

 

Note that these are warnings and not errors. An error tells you that the compiler/linker cannot generate any output. A warning tells you that code has been generated but that there may be problems with it and that you should investigate further.

 

PS

This is with the Codevision compiler, posts above this one tell you how to enable such features in GCC.

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


Yup, -ffunction-sections, -fdata-sections, -gc-sections will have the same result:

 

 

As it happens GCC has removed BOTH functions here. Another view of the same here:

 .text.used_function
                0x00000000        0xe main.o
 .text.unused_function
                0x00000000        0xe main.o

That is because it inlined the one that was used then, having first also created a "callable" copy (in case any other compilation unit accessed the non-static function) it then discarded the callable one as that was not referenced externally.

 

Proper use of "static" is to be encouraged to help the compiler make this kind of decision earlier. With that neither function is created, only the one is inlined but, of course, the downside of static is that limited name scope means they don't even get a mention in the .MAP