Jump to content


[VC++] Identifying dead code


11 replies to this topic

#1 .oisyn

    DevMaster Staff

  • Moderators
  • 1810 posts

Posted 27 June 2006 - 11:05 AM

I'm currently doing some major refactoring in our current codebase. I've plugged in our new cross-platform renderer and are removing the old legacy rendering pipeline. While at a high level all the references have been removed, and most of the legacy sourcefiles have been deleted, I'm not quite sure if I've tagged all the necessary functions.

The VC++ linker has the option to not include unreferenced symbols in the final executable. While this yields a pretty fine result, it obviously doesn't remove the original code. I've checked the documentation, but there does not seem to be an option to identify those unreferenced symbols (which is not that weird as this is actually the other way around with respect to how the linker works - it just keeps on introducing symbols that are already referenced, it doesn't keep a database of which symbols aren't).

It shouldn't be too hard to build a tool that checks the symbols in the final binary against all symbols in the object files used to build that binary, which makes me believe that such a tool should exist out there. A google search yielded nothing, however.

My last resort is to do a diff against two exported .map files by the linker - one with the 'remove unreferenced symbols' option enabled and the other disabled. But I'd rather have a tool that produces a nice list of symbols, so if anyone has some experience in this field I'm open to suggestions ;)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#2 Nick

    Senior Member

  • Members
  • PipPipPipPip
  • 1225 posts

Posted 27 June 2006 - 11:39 AM

Interesting question. There are lots of tools for code coverage, but they are not conservative. They detect which functions are called for specific test cases only. But it could be of help to eliminate dead code from the source if you're careful...

Finding the differences between .map files seems most effective for a conservative approach. Maybe some diff tool can show only the differences?

#3 dave_

    Senior Member

  • Members
  • PipPipPipPip
  • 584 posts

Posted 27 June 2006 - 11:52 AM

Using msvc try /VERBOSE:REF
I'm not sure what it does but its worth a try.

#4 .oisyn

    DevMaster Staff

  • Moderators
  • 1810 posts

Posted 27 June 2006 - 12:43 PM

Is that a vs 2003 option? It isn't mentioned in the docs (only :LIB and :SAFESEH). I'll give it a try though, but I think it only reports the refenced symbols, not the unreferenced ones ;)

.edit: LINK : fatal error LNK1117: syntax error in option 'VERBOSE:REF'

.edit2: it's actually vs 2005 :)

Quote

/VERBOSE:REF
Display information about linker activity resulting from the use of /OPT:REF
Unfortunately, we don't use vs 2005 yet
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#5 eddie

    Senior Member

  • Members
  • PipPipPipPip
  • 751 posts

Posted 27 June 2006 - 03:21 PM

Couldn't you use a combination of dumpbin /SYMBOLS from a non stripped and a stripped binary, and then simply diff them?

Not uber fancy, but it should do the trick.

#6 dave_

    Senior Member

  • Members
  • PipPipPipPip
  • 584 posts

Posted 27 June 2006 - 03:48 PM

.oisyn said:

Unfortunately, we don't use vs 2005 yet

Unlucky sounds like what you need. Perhaps you could download the express edition and try it on that... although it might be a bit of hassle getting the code to compile

#7 .oisyn

    DevMaster Staff

  • Moderators
  • 1810 posts

Posted 27 June 2006 - 04:02 PM

I have 2005 installed on my machine, that isn't the problem. Compiling with it, though, is, as we use our own build system that works with 2003, not 2005 ;)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#8 pater

    Valued Member

  • Members
  • PipPipPip
  • 117 posts

Posted 27 June 2006 - 08:09 PM

What exactly is the problem with using the diff command? If it's only because the map file contains mangled names, there's the trick: use the "undname" tool (lies somewhere within the MSVC install folder). It can de-mangle any symbol passed as an argument.

#9 .oisyn

    DevMaster Staff

  • Moderators
  • 1810 posts

Posted 27 June 2006 - 08:31 PM

Not a real problem, but I have to link twice with different options (and I have to manually change those options in the build scripts), and I don't know how 'diffable' it really is (haven't tried it yet, but if the linker chooses another order of symbols I'm basically screwed... although I can write a simple tool that parses all the symbols and puts them in an ordered set, and do a diff on that). Thanks for the unmangle tip though.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#10 .oisyn

    DevMaster Staff

  • Moderators
  • 1810 posts

Posted 29 June 2006 - 04:22 PM

For future reference, the DbgHelp API implements the function to undecorate a C++ symbol, it is called UnDecorateSymbolName ;)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#11 dave_

    Senior Member

  • Members
  • PipPipPipPip
  • 584 posts

Posted 03 July 2006 - 12:23 PM

.oisyn said:

although I can write a simple tool that parses all the symbols and puts them in an ordered set, and do a diff on that.

I recommend using excel if you've got it.

#12 .oisyn

    DevMaster Staff

  • Moderators
  • 1810 posts

Posted 03 July 2006 - 12:53 PM

Heh, I was just going to kick this thread with the code of my just written tool, when I saw your reaction. Too late :)

I wrote this in just a few minutes, it "parses" the .map files that you give on the command-line, and outputs the difference between the sets to standard out, while undecorating the symbol names. Alas, no comments, but it should be easy to understand as it relies heavily on the STL and it's algorithms.

.edit: some bugfixes, and eliminates all std:: stuff and literals that need memory (like strings and floats). Now sorts by identifier instead of decorated name, and displays only the part of a symbol that makes sense (identifier and function signature, no more static/virtual/__thiscall crap or function return types) :)

#include <iostream>
#include <fstream>
#include <set>
#include <algorithm>
#include <string>
#include <sstream>
#include <vector>
#include <iterator>

#include <windows.h>
#include <Dbghelp.h>

#pragma comment(lib, "dbghelp.lib")


struct symbol
{
	std::string name;
	std::string object;
	std::string nameOnly;

	symbol() { }
	symbol(const std::string & name, const std::string & object) : name(name), object(object)
	{
		static char buf[1024];
		UnDecorateSymbolName(name.c_str(), buf, sizeof(buf), UNDNAME_NAME_ONLY);
		nameOnly = buf;
	}

	bool operator < (const symbol & r) const
	{
		return name < r.name;
	}

	std::string UndecoratedName() const
	{
		static char buf[1024];
		UnDecorateSymbolName(name.c_str(), buf, sizeof(buf), UNDNAME_NO_ALLOCATION_LANGUAGE | UNDNAME_NO_ACCESS_SPECIFIERS | UNDNAME_NO_FUNCTION_RETURNS | UNDNAME_NO_MEMBER_TYPE);
		return buf;
	}
};


void read(std::set<symbol> & s, const char * file)
{
	std::ifstream f(file);
	std::string line;

	while (std::getline(f, line))
		if (!line.compare(0, 9, "  Address"))
			break;

	if (!f)
		return;

	std::vector<std::string> words;
	while (std::getline(f, line))
	{
		words.clear();
		std::istringstream lineStream(line);
		std::copy(std::istream_iterator<std::string>(lineStream), std::istream_iterator<std::string>(), std::back_inserter(words));

		if (words.size() < 4)
			continue;

		symbol sym(words[1], words.back());
		if (sym.nameOnly.empty() ||
			sym.nameOnly[0] == '$' ||
			!sym.nameOnly.compare(0, 5, "std::") ||
			!sym.nameOnly.compare(0, 8, "`string'"))
			continue;

		s.insert(sym);
	}
}

bool sortbyundecoratedname(const symbol & l, const symbol & r)
{
	if (l.nameOnly != r.nameOnly)
		return l.nameOnly < r.nameOnly;
	return l < r;
}

void outputsymbol (const symbol & s)
{
	std::cout << s.UndecoratedName() << " [" << s.object << "]" << std::endl;
}

int main(int argc, char ** argv)
{
	if (argc < 3)
	{
		std::cout << "usage: " << argv[0] << " [mapfile1] [mapfile2]" << std::endl;
		return 0;
	}

	std::set<symbol> s1, s2;
	read(s1, argv[1]);
	read(s2, argv[2]);

	std::vector<symbol> diff;
	std::set_difference(s1.begin(), s1.end(), s2.begin(), s2.end(), std::back_inserter(diff));
	std::sort(diff.begin(), diff.end(), sortbyundecoratedname);

	std::for_each(diff.begin(), diff.end(), outputsymbol);
}

C++ addict
-
Currently working on: the 3D engine for Tomb Raider.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users