Thread: Best way to find out if a function got inlined

  1. #1
    Registered User
    Join Date
    Nov 2006
    Posts
    519

    Best way to find out if a function got inlined

    Hi,

    what is they easiest way for VS2005? I hope there is something better than hand-parsing the release mode generated machine code

    Thank you!

  2. #2
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by pheres View Post
    Hi,

    what is they easiest way for VS2005? I hope there is something better than hand-parsing the release mode generated machine code

    Thank you!
    I have to do this pretty often. I do it by examining the generated code manually. I don't know of any better way to do it, and even if the compiler could somehow report this information I'm not sure that I'd trust it.

    A function might get inlined in some places but not inlined in others. The only way to know if it was inlined in the place you are concerned with is to check it yourself.

    The compiler probably has an (unportable) method to force inlining if that's really what you want.

    EDIT: Theoretically, if the inline function is module-static, and was inlined everywhere it was called, and the proper compiler switches are set, then there should be no symbol in the generated object file corresponding to that inline function. But again, I would not trust this 100%
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  3. #3
    Registered User
    Join Date
    Nov 2006
    Posts
    519
    Thank you.
    My assembler is even worse than my C++. I've to let the compiler put out the generated assembler code and watch out for "call" commands, is that correct?

  4. #4
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    If you're using the Visual Studio toolchain you can leverage the symbol handler functions to parse through the pdb file to see if it a function / global exists in the binary. The following code works for me. It finds the symbol in the debug exe where the function hasn't been inlined but fails to find it for the release exe when it has. Brewbucks caviat that it is dependent on the compiler/linker removing dead code applies but VS does this with default release settings. As a bonus SymEnumSymbols takes wildcards so you can list a subset of symbols or all of them via the second command line argument.

    This does require an updated version of the dbghelp dll as the default version that ships with all versions of 2000 and XP can't handle private symbols. A sufficiently updated version ships with Visual Studio (in the common7/IDE directory) and the latest is downloadble from Debugging Tools for Windows - Overview in the debugging tools for windows package. The file name of the dll or the name passed to loadlibrary in the code will have to be modified so they match.

    Code:
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <dbghelp.h>
    #include <iostream>
    #include <cstring>
    #include <new>
    
    // here be typedefs and definitions of imported functions
    typedef DWORD (WINAPI*SetOptsFunc)(DWORD);
    typedef BOOL (WINAPI*InitFunc)(HANDLE, LPCSTR, BOOL);
    typedef DWORD64 (WINAPI*LoadFunc)(HANDLE, HANDLE, LPCSTR, LPCSTR, DWORD64, DWORD, PMODLOAD_DATA, DWORD);
    typedef BOOL (WINAPI*EnumFunc)(HANDLE, DWORD64, LPCSTR, PSYM_ENUMERATESYMBOLS_CALLBACK, PVOID);
    typedef BOOL (WINAPI*CleanupFunc)(HANDLE);
    SetOptsFunc symSetOptions = NULL;
    InitFunc symInitialize = NULL;
    LoadFunc symLoadModuleEx = NULL;
    EnumFunc symEnumSymbols = NULL;
    CleanupFunc symCleanup = NULL;
    
    bool LoadImports(HMODULE hDbghelp)
    {
        symSetOptions = reinterpret_cast<SetOptsFunc>(GetProcAddress(hDbghelp, "SymSetOptions"));
        symInitialize = reinterpret_cast<InitFunc>(GetProcAddress(hDbghelp, "SymInitialize"));
        symLoadModuleEx = reinterpret_cast<LoadFunc>(GetProcAddress(hDbghelp, "SymLoadModuleEx"));
        symEnumSymbols = reinterpret_cast<EnumFunc>(GetProcAddress(hDbghelp, "SymEnumSymbols"));
        symCleanup = reinterpret_cast<CleanupFunc>(GetProcAddress(hDbghelp, "SymCleanup"));
        return symSetOptions && symInitialize && symLoadModuleEx && symEnumSymbols && symCleanup;
    }
    
    void DisplayFlags(PSYMBOL_INFO pSym, std::ostream& out)
    {
        DWORD flags = pSym->Flags;
        if(flags & SYMFLAG_REGISTER)
        {
            out << "\tregister\n";
        }
        if(flags & SYMFLAG_PARAMETER)
        {
            out << "\tfunction parameter\n";
        }
        if(flags & SYMFLAG_LOCAL)
        {
            out << "\tlocal variable\n";
        }
        if(flags & SYMFLAG_CONSTANT)
        {
            out << "\tconstant of value " << pSym->Value << '\n';
        }
        if(flags & SYMFLAG_EXPORT)
        {
            out << "\tmodule export\n";
        }
        if(flags & SYMFLAG_FORWARDER)
        {
            out << "\ta forwarder\n";
        }
        if(flags & SYMFLAG_FUNCTION)
        {
            out << "\tfunction\n";
        }
        if(flags & SYMFLAG_THUNK)
        {
            out << "\tthunk\n";
        }
        if(flags & SYMFLAG_TLSREL)
        {
            out << "\toffset into the TLS area\n";
        }
        if(flags & SYMFLAG_SLOT)
        {
            out << "\tmanaged code slot\n";
        }
        if(flags & SYMFLAG_ILREL)
        {
            out << "\taddress relative to the IL block\n";
        }
        if(flags & SYMFLAG_METADATA)
        {
            out << "\tchunk of managed metadata\n";
        }
        if(flags & SYMFLAG_CLR_TOKEN)
        {
            out << "\tCLR token\n";
        }
    }
    
    // see http://msdn.microsoft.com/en-us/library/bkedss5f.aspx for more
    // info on what the typeTags mean
    const char* typeTags[] = {
        "",
        "Executable (Global)",
        "Compiland", 
        "CompilandDetails", 
        "CompilandEnv",
        "Function", 
        "Block",
        "Data",
        "Unused", 
        "Labe", 
        "PublicSymbol", 
        "UDT", 
        "Enum", 
        "FunctionType", 
        "PointerType", 
        "ArrayType", 
        "BaseType", 
        "Typedef", 
        "BaseClass",
        "Friend",
        "FunctionArgType", 
        "FuncDebugStart", 
        "FuncDebugEnd",
        "UsingNamespace", 
        "VTableShape",
        "VTable",
        "Custom",
        "Thunk",
        "CustomType",
        ""
    };
     
    BOOL CALLBACK SymFound(PSYMBOL_INFO pSymInfo, ULONG, PVOID pFoundFlag)
    {
        *(static_cast<bool*>(pFoundFlag)) = true;
        std::cout << "Found symbol\n";
        std::cout << "Name: " << pSymInfo->Name << '\n';
        if(pSymInfo->Address)
        {
            std::cout << "Address: 0x" << std::hex << pSymInfo->Address << '\n';
        }
        if(pSymInfo->Flags)
        {
            std::cout << "Type Flags: ";
            DisplayFlags(pSymInfo, std::cout);
        }
        std::cout << "Type Tag: " << typeTags[pSymInfo->Tag] << '\n';
        std::cout << '\n';
        return TRUE;
    }
    
    int main(int argc, char** argv)
    {
        if(argc >= 2)
        {
            HMODULE hDbghelp = LoadLibrary(L"dbghelp_6.9.dll");
            if(hDbghelp && LoadImports(hDbghelp))
            {
                HANDLE hProc = GetCurrentProcess();
                symSetOptions(SYMOPT_ALLOW_ZERO_ADDRESS | SYMOPT_AUTO_PUBLICS | SYMOPT_CASE_INSENSITIVE | 
                SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME |
                SYMOPT_OMAP_FIND_NEAREST);
                if(symInitialize(hProc, 0, FALSE))
                {
                    HANDLE hFile = CreateFileA(argv[1], GENERIC_READ, 0, 
                    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
                    if(hFile != INVALID_HANDLE_VALUE)
                    {
                        DWORD64 baseAddress = 0;
                        if((baseAddress = symLoadModuleEx(hProc, hFile, argv[1],
                        NULL, 0, GetFileSize(hFile, NULL), NULL, 0)))
                        {
                            bool anyFound = false;
                            const char* matchString = argv[2] ? argv[2] : "*";
                            if(symEnumSymbols(hProc, baseAddress, matchString, &SymFound, &anyFound))
                            {
                                if(!anyFound)
                                {
                                    std::cout << "No symbols of pattern " << matchSymbolBuf << " found\n";
                                }
                            }
                            else std::cout << "Failed to enumerate symbols - error " << GetLastError() << '\n';
                        }
                        else std::cout << "Couldn't load image module and symbols - error " << GetLastError();
                        CloseHandle(hFile);
                    }
                    else std::cout << "Couldn't open image file - error " << GetLastError();
                    symCleanup(hProc);
                    FreeLibrary(hDbghelp);
                }
                else std::cout << "Couldn't load or find symbols in dbghelp.dll";
            }
            else std::cout << "Couldn't initialize the symbol handler";
        }
        else
        {
            // symbol name is case insensitive and can take 
            // multiple wildcards so that "MySym", "*Sym" and "*Sym*"
            // are all valid and will match MySym and mysym
            std::cout << "Usage: SymFinder [image file] <optional symbol name>";
        }
        std::cout << std::endl;
    }
    Last edited by adeyblue; 03-27-2009 at 08:15 PM.

  5. #5
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    In visual studio you can use:
    Code:
    #pragma warning(1: 4710)
    However, it has the consequence that ANY inline-function that isn't actually inlined will be reported. So when I wrote a small testcase of about 10 lines of code, I got 16 warnings, not a single one for my code (because I hadn't got to "make this function non-inline") - the warnings are all to do with iostream functions that aren't inlined for some reason.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Seg Fault in Compare Function
    By tytelizgal in forum C Programming
    Replies: 1
    Last Post: 10-25-2008, 03:06 PM
  2. In over my head
    By Shelnutt2 in forum C Programming
    Replies: 1
    Last Post: 07-08-2008, 06:54 PM
  3. Game Pointer Trouble?
    By Drahcir in forum C Programming
    Replies: 8
    Last Post: 02-04-2006, 02:53 AM
  4. Problem with Visual C++ Object-Oriented Programming Book.
    By GameGenie in forum C++ Programming
    Replies: 9
    Last Post: 08-29-2005, 11:21 PM
  5. Replies: 5
    Last Post: 02-08-2003, 07:42 PM