<commit_before>/*************************************************************************
 *                                                                       *
 *  I|*j^3Cl|a   "+!*%                  qt          Nd   gW              *
 *  l]{y+l?MM*  !#Wla\NNP               NW          MM   I|              *
 *        PW    ?E|    tWg              Wg  sC!     AW           ~@v~    *
 *       NC     ?M!    yN|  WW     MK   MW@K1Y%M@   RM   #Q    QP@tim    *
 *     CM|      |WQCljAE|   MD     Mg   RN     cM~  NM   WQ   MQ         *
 *    #M        aQ?         MW     M3   Mg      Q(  HQ   YR  IM|         *
 *   Dq         {Ql         MH    iMX   Mg     MM   QP   QM   Eg         *
 * !EWNaPRag2$  +M"          $WNaHaN%   MQE$%EXW    QQ   CM    %M%a$D    *
 *                                                                       *
 *               Website: https://github.com/zpublic/zpublic             *
 *                                                                       *
 ************************************************************************/

/**
 * @file
 * @brief ļ汾Ϣ
 */

#pragma once
#pragma comment(lib, "Version.lib")

#include "win_utils_header.h"

namespace zl
{
namespace WinUtils
{
/**
* @brief ļ汾Ϣ࣬ԻȡļĸϢ
*/
    class ZLFileVersion
    {
    public:
        ZLFileVersion()
        {
            Reset();
        }

        virtual ~ZLFileVersion()
        {}
	/**
	 * @brief ȡļ汾Ϣ
	 * @param[in]   hModule ļĬΪNULL
	 * @return ɹTRUEʧܷFALSE
	 * @see Create
	 */
        BOOL Create(HMODULE hModule = NULL)
        {
            CString	sPath;

            GetModuleFileName(hModule, sPath.GetBuffer(_MAX_PATH), _MAX_PATH);
            sPath.ReleaseBuffer();

            return Create(sPath);
        }
	/**
	 * @brief ȡļϢ
	 * @param[in]   lpszFileName  ļ
	 * @return ɹTRUEʧܷFALSE
	 * @see GetFileVersionInfoSize
	 */
        BOOL Create(LPCTSTR lpszFileName)
        {
            Reset();

            DWORD dwHandle;
            DWORD dwFileVersionInfoSize = GetFileVersionInfoSize((LPTSTR)lpszFileName, &dwHandle);

            if (!dwFileVersionInfoSize)
                return FALSE;

            LPVOID lpData = (LPVOID)new BYTE[dwFileVersionInfoSize];
            if (lpData == NULL)
                return FALSE;

            try
            {
                if (!GetFileVersionInfo((LPTSTR)lpszFileName, dwHandle, dwFileVersionInfoSize, lpData))
                    throw FALSE;

                LPVOID lpInfo;
                UINT unInfoLen;

                if (VerQueryValue(lpData, L"\\", &lpInfo, &unInfoLen))
                {
                    if (unInfoLen == sizeof(mFileInfo))
                        memcpy(&mFileInfo, lpInfo, unInfoLen);
                }

                VerQueryValue(lpData, L"\\VarFileInfo\\Translation", &lpInfo, &unInfoLen);

                DWORD dwLangCode = 0;
                BOOL bRetCode;

                bRetCode = GetTranslationId(lpInfo, unInfoLen, GetUserDefaultLangID(), dwLangCode, FALSE);
                if (bRetCode == FALSE)
                {
                    bRetCode = GetTranslationId(lpInfo, unInfoLen, GetUserDefaultLangID(), dwLangCode, TRUE);
                    if (bRetCode == FALSE)
                    {
                        bRetCode = GetTranslationId(lpInfo, unInfoLen, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), dwLangCode, TRUE);
                        if (bRetCode == FALSE)
                        {
                            bRetCode = GetTranslationId(lpInfo, unInfoLen, MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), dwLangCode, TRUE);
                            if (bRetCode == FALSE)
                            {
                                dwLangCode = *((DWORD*)lpInfo);
                            }
                        }
                    }
                }

                CString sSubBlock;
                CString strTemp;

                sSubBlock.Format(L"\\StringFileInfo\\%04X%04X\\", dwLangCode & 0x0000FFFF, (dwLangCode & 0xFFFF0000)>>16);

                strTemp = sSubBlock + L"CompanyName";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == FALSE)//Ƿ
                    dwLangCode = 0x04b00409;//sogou
                strTemp.ReleaseBuffer();

                sSubBlock.Format(L"\\StringFileInfo\\%04X%04X\\", dwLangCode & 0x0000FFFF, (dwLangCode & 0xFFFF0000)>>16);

                strTemp = sSubBlock + L"CompanyName";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msCompanyName = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"FileDescription";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msFileDescription = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"FileVersion";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msFileVersion = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"InternalName";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msInternalName = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"LegalCopyright";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msLegalCopyright = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"OriginalFileName";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msOriginalFileName = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"ProductName";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msProductName = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"ProductVersion";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msProductVersion = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"Comments";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msComments = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"LegalTrademarks";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msLegalTrademarks = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"PrivateBuild";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msPrivateBuild = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"SpecialBuild";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msSpecialBuild = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                delete[] lpData;
            }catch (BOOL)
            {
                delete[] lpData;
                return FALSE;
            }

            return TRUE;
        }
	/**
	 * @brief ȡļİ汾Ϣ
	 * @param[in]   nIndex
	 * @return ɹط㣬ʧܷ
	 */
        WORD GetFileVersion(int nIndex) const
        {
            if (nIndex == 0)
                return (WORD)(mFileInfo.dwFileVersionLS & 0x0000FFFF);
            else if (nIndex == 1)
                return (WORD)((mFileInfo.dwFileVersionLS & 0xFFFF0000) >> 16);
            else if (nIndex == 2)
                return (WORD)(mFileInfo.dwFileVersionMS & 0x0000FFFF);
            else if (nIndex == 3)
                return (WORD)((mFileInfo.dwFileVersionMS & 0xFFFF0000) >> 16);
            else
                return 0;
        }
	/**
	 * @brief ȡļProductϢ
	 * @param[in]   nIndex
	 * @return ɹط㣬ʧܷ
	 */
        WORD GetProductVersion(int nIndex) const
        {
            if (nIndex == 0)
                return (WORD)(mFileInfo.dwProductVersionLS & 0x0000FFFF);
            else if (nIndex == 1)
                return (WORD)((mFileInfo.dwProductVersionLS & 0xFFFF0000) >> 16);
            else if (nIndex == 2)
                return (WORD)(mFileInfo.dwProductVersionMS & 0x0000FFFF);
            else if (nIndex == 3)
                return (WORD)((mFileInfo.dwProductVersionMS & 0xFFFF0000) >> 16);
            else
                return 0;
        }
	/**
	 * @brief ȡļFileFlagsMask
	 * @param[in]   nIndex
	 * @return FileFlagsMask
	 */
        DWORD GetFileFlagsMask() const
        {
            return mFileInfo.dwFileFlagsMask;
        }
	/**
	 * @brief ȡļFileFlags
	 * @return  FileFlags
	 */
        DWORD GetFileFlags() const
        {
            return mFileInfo.dwFileFlags;
        }
	/**
	 * @brief ȡļϵͳ
	 * @param[in]   nIndex
	 * @return FileOS
	 */
        DWORD GetFileOs() const
        {
            return mFileInfo.dwFileOS;
        }
	/**
	 * @brief ȡļͨ
	 * @return FileType
	 */
        DWORD GetFileType() const
        {
            return mFileInfo.dwFileType;
        }
	/**
	 * @brief ȡļ
	 * @return FileSubtype
	 */
        DWORD GetFileSubtype() const
        {
            return mFileInfo.dwFileSubtype;
        }
	/**
	 * @brief ȡļ
	 * @return FILETIME data structure
	 */
        FILETIME GetFileDate() const
        {
            FILETIME ft;

            ft.dwLowDateTime = mFileInfo.dwFileDateLS;
            ft.dwHighDateTime = mFileInfo.dwFileDateMS;

            return ft;
        }
	/**
	 * @brief ȡCompanyName
	 * @return  ַCompanyName
	 */
        CString GetCompanyName() const
        {
            return msCompanyName;
        }
	/**
	 * @brief ȡļ
	 * @return  ַʾļ
	 */
        CString GetFileDescription() const
        {
            return msFileDescription;
        }
	/**
	 * @brief ȡļ汾
	 * @return  ַFileVersion
	 */
        CString GetFileVersion() const
        {
            return msFileVersion;
        }
	/**
	 * @brief ȡInternalName
	 * @return  ַInternalName
	 */
        CString GetInternalName() const
        {
            return msInternalName;
        }
	/**
	 * @brief ȡLegalCopyright
	 * @return  ַLegalCopyright
	 */
        CString GetLegalCopyright() const
        {
            return msLegalCopyright;
        }
	/**
	 * @brief ȡOriginalFileName
	 * @return  ַOriginalFileName
	 */
        CString GetOriginalFileName() const
        {
            return msOriginalFileName;
        }
	/**
	 * @brief ȡProductName
	 * @return  ַProductName
	 */
        CString GetProductName() const
        {
            return msProductName;
        }
	/**
	 * @brief ȡProductVersion
	 * @return  ַProductVersion
	 */
        CString GetProductVersion() const
        {
            return msProductVersion;
        }
	/**
	 * @brief ȡComments
	 * @return  ַComments
	 */
        CString GetComments() const
        {
            return msComments;
        }
	/**
	 * @brief ȡLegalTrademarks
	 * @return  ַLegalTrademarks
	 */
        CString GetLegalTrademarks() const
        {
            return msLegalTrademarks;
        }
	/**
	 * @brief ȡPrivateBuild
	 * @return  ַPrivateBuild
	 */
        CString GetPrivateBuild() const
        {
            return msPrivateBuild;
        }
	/**
	 * @brief ȡSpecialBuild
	 * @return  ַSpecialBuild
	 */
        CString GetSpecialBuild() const
        {
            return msSpecialBuild;
        }

    protected:
        virtual void Reset()
        {
            ZeroMemory(&mFileInfo, sizeof(mFileInfo));

            msCompanyName.Empty();
            msFileDescription.Empty();
            msFileVersion.Empty();
            msInternalName.Empty();
            msLegalCopyright.Empty();
            msOriginalFileName.Empty();
            msProductName.Empty();
            msProductVersion.Empty();
            msComments.Empty();
            msLegalTrademarks.Empty();
            msPrivateBuild.Empty();
            msSpecialBuild.Empty();
        }ȡǰûıԱʾ
	/**
	 * @brief ȡǰûıԱʾ
	 * @param[in]   lpData Bufferָļİ汾Ϣ
	 * @param[in]   unBlockSize VS_FIXEDFILEINFOṹĴС
	 * @param[in]  wLangId Աʾ
	 * @param[out]  dwId Աʾ
	 * @param[in]  bPrimaryEnough Ƿеֽڲ
	 * @return ɹTRUEʧܷFALSE
	 */
        BOOL GetTranslationId(
            LPVOID lpData,
            UINT   unBlockSize,
            WORD   wLangId,
            DWORD  &dwId,
            BOOL   bPrimaryEnough = FALSE)
        {
            LPWORD lpwData;

            for (lpwData = (LPWORD)lpData; (LPBYTE)lpwData < ((LPBYTE)lpData)+unBlockSize; lpwData+=2)
            {
                if (*lpwData == wLangId)
                {
                    dwId = *((DWORD*)lpwData);
                    return TRUE;
                }
            }

            if (!bPrimaryEnough)
                return FALSE;

            for (lpwData = (LPWORD)lpData; (LPBYTE)lpwData < ((LPBYTE)lpData)+unBlockSize; lpwData+=2)
            {
                if (((*lpwData)&0x00FF) == (wLangId&0x00FF))
                {
                    dwId = *((DWORD*)lpwData);
                    return TRUE;
                }
            }

            return FALSE;
        }

    private:
        VS_FIXEDFILEINFO mFileInfo;

        CString msCompanyName;
        CString msFileDescription;
        CString msFileVersion;
        CString msInternalName;
        CString msLegalCopyright;
        CString msOriginalFileName;
        CString msProductName;
        CString msProductVersion;
        CString msComments;
        CString msLegalTrademarks;
        CString msPrivateBuild;
        CString msSpecialBuild;
    };

}
}
<commit_msg>*file_version文件注释<commit_after>/*************************************************************************
 *                                                                       *
 *  I|*j^3Cl|a   "+!*%                  qt          Nd   gW              *
 *  l]{y+l?MM*  !#Wla\NNP               NW          MM   I|              *
 *        PW    ?E|    tWg              Wg  sC!     AW           ~@v~    *
 *       NC     ?M!    yN|  WW     MK   MW@K1Y%M@   RM   #Q    QP@tim    *
 *     CM|      |WQCljAE|   MD     Mg   RN     cM~  NM   WQ   MQ         *
 *    #M        aQ?         MW     M3   Mg      Q(  HQ   YR  IM|         *
 *   Dq         {Ql         MH    iMX   Mg     MM   QP   QM   Eg         *
 * !EWNaPRag2$  +M"          $WNaHaN%   MQE$%EXW    QQ   CM    %M%a$D    *
 *                                                                       *
 *               Website: https://github.com/zpublic/zpublic             *
 *                                                                       *
 ************************************************************************/

/**
 * @file
 * @brief ļ汾Ϣ
 */

#pragma once
#pragma comment(lib, "Version.lib")

#include "win_utils_header.h"

namespace zl
{
namespace WinUtils
{
/**
* @brief ļ汾Ϣ࣬ԻȡļĸϢ
*/
    class ZLFileVersion
    {
    public:
        ZLFileVersion()
        {
            Reset();
        }

        virtual ~ZLFileVersion()
        {}
	/**
	 * @brief ȡļ汾Ϣ
	 * @param[in]   hModule ļĬΪNULL
	 * @return ɹTRUEʧܷFALSE
	 * @see Create
	 */
        BOOL Create(HMODULE hModule = NULL)
        {
            CString	sPath;

            GetModuleFileName(hModule, sPath.GetBuffer(_MAX_PATH), _MAX_PATH);
            sPath.ReleaseBuffer();

            return Create(sPath);
        }
	/**
	 * @brief ȡļϢ
	 * @param[in]   lpszFileName  ļ
	 * @return ɹTRUEʧܷFALSE
	 * @see GetFileVersionInfoSize
	 */
        BOOL Create(LPCTSTR lpszFileName)
        {
            Reset();

            DWORD dwHandle;
            DWORD dwFileVersionInfoSize = GetFileVersionInfoSize((LPTSTR)lpszFileName, &dwHandle);

            if (!dwFileVersionInfoSize)
                return FALSE;

            LPVOID lpData = (LPVOID)new BYTE[dwFileVersionInfoSize];
            if (lpData == NULL)
                return FALSE;

            try
            {
                if (!GetFileVersionInfo((LPTSTR)lpszFileName, dwHandle, dwFileVersionInfoSize, lpData))
                    throw FALSE;

                LPVOID lpInfo;
                UINT unInfoLen;

                if (VerQueryValue(lpData, L"\\", &lpInfo, &unInfoLen))
                {
                    if (unInfoLen == sizeof(mFileInfo))
                        memcpy(&mFileInfo, lpInfo, unInfoLen);
                }

                VerQueryValue(lpData, L"\\VarFileInfo\\Translation", &lpInfo, &unInfoLen);

                DWORD dwLangCode = 0;
                BOOL bRetCode;

                bRetCode = GetTranslationId(lpInfo, unInfoLen, GetUserDefaultLangID(), dwLangCode, FALSE);
                if (bRetCode == FALSE)
                {
                    bRetCode = GetTranslationId(lpInfo, unInfoLen, GetUserDefaultLangID(), dwLangCode, TRUE);
                    if (bRetCode == FALSE)
                    {
                        bRetCode = GetTranslationId(lpInfo, unInfoLen, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), dwLangCode, TRUE);
                        if (bRetCode == FALSE)
                        {
                            bRetCode = GetTranslationId(lpInfo, unInfoLen, MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), dwLangCode, TRUE);
                            if (bRetCode == FALSE)
                            {
                                dwLangCode = *((DWORD*)lpInfo);
                            }
                        }
                    }
                }

                CString sSubBlock;
                CString strTemp;

                sSubBlock.Format(L"\\StringFileInfo\\%04X%04X\\", dwLangCode & 0x0000FFFF, (dwLangCode & 0xFFFF0000)>>16);

                strTemp = sSubBlock + L"CompanyName";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == FALSE)//Ƿ
                    dwLangCode = 0x04b00409;//sogou
                strTemp.ReleaseBuffer();

                sSubBlock.Format(L"\\StringFileInfo\\%04X%04X\\", dwLangCode & 0x0000FFFF, (dwLangCode & 0xFFFF0000)>>16);

                strTemp = sSubBlock + L"CompanyName";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msCompanyName = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"FileDescription";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msFileDescription = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"FileVersion";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msFileVersion = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"InternalName";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msInternalName = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"LegalCopyright";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msLegalCopyright = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"OriginalFileName";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msOriginalFileName = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"ProductName";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msProductName = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"ProductVersion";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msProductVersion = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"Comments";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msComments = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"LegalTrademarks";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msLegalTrademarks = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"PrivateBuild";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msPrivateBuild = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                strTemp = sSubBlock + L"SpecialBuild";
                bRetCode = VerQueryValue(lpData, strTemp.GetBuffer(), &lpInfo, &unInfoLen);
                if (bRetCode == TRUE)
                    msSpecialBuild = (LPCTSTR)lpInfo;
                strTemp.ReleaseBuffer();

                delete[] lpData;
            }catch (BOOL)
            {
                delete[] lpData;
                return FALSE;
            }

            return TRUE;
        }
	/**
	 * @brief ȡļİ汾Ϣ
	 * @param[in]   nIndex
	 * @return ɹط㣬ʧܷ
	 */
        WORD GetFileVersion(int nIndex) const
        {
            if (nIndex == 0)
                return (WORD)(mFileInfo.dwFileVersionLS & 0x0000FFFF);
            else if (nIndex == 1)
                return (WORD)((mFileInfo.dwFileVersionLS & 0xFFFF0000) >> 16);
            else if (nIndex == 2)
                return (WORD)(mFileInfo.dwFileVersionMS & 0x0000FFFF);
            else if (nIndex == 3)
                return (WORD)((mFileInfo.dwFileVersionMS & 0xFFFF0000) >> 16);
            else
                return 0;
        }
	/**
	 * @brief ȡļProductϢ
	 * @param[in]   nIndex
	 * @return ɹط㣬ʧܷ
	 */
        WORD GetProductVersion(int nIndex) const
        {
            if (nIndex == 0)
                return (WORD)(mFileInfo.dwProductVersionLS & 0x0000FFFF);
            else if (nIndex == 1)
                return (WORD)((mFileInfo.dwProductVersionLS & 0xFFFF0000) >> 16);
            else if (nIndex == 2)
                return (WORD)(mFileInfo.dwProductVersionMS & 0x0000FFFF);
            else if (nIndex == 3)
                return (WORD)((mFileInfo.dwProductVersionMS & 0xFFFF0000) >> 16);
            else
                return 0;
        }
	/**
	 * @brief ȡļFileFlagsMask
	 * @param[in]   nIndex
	 * @return FileFlagsMask
	 */
        DWORD GetFileFlagsMask() const
        {
            return mFileInfo.dwFileFlagsMask;
        }
	/**
	 * @brief ȡļFileFlags
	 * @return  FileFlags
	 */
        DWORD GetFileFlags() const
        {
            return mFileInfo.dwFileFlags;
        }
	/**
	 * @brief ȡļϵͳ
	 * @param[in]   nIndex
	 * @return FileOS
	 */
        DWORD GetFileOs() const
        {
            return mFileInfo.dwFileOS;
        }
	/**
	 * @brief ȡļͨ
	 * @return FileType
	 */
        DWORD GetFileType() const
        {
            return mFileInfo.dwFileType;
        }
	/**
	 * @brief ȡļ
	 * @return FileSubtype
	 */
        DWORD GetFileSubtype() const
        {
            return mFileInfo.dwFileSubtype;
        }
	/**
	 * @brief ȡļ
	 * @return FILETIME data structure
	 */
        FILETIME GetFileDate() const
        {
            FILETIME ft;

            ft.dwLowDateTime = mFileInfo.dwFileDateLS;
            ft.dwHighDateTime = mFileInfo.dwFileDateMS;

            return ft;
        }
	/**
	 * @brief ȡCompanyName
	 * @return  ַCompanyName
	 */
        CString GetCompanyName() const
        {
            return msCompanyName;
        }
	/**
	 * @brief ȡļ
	 * @return  ַʾļ
	 */
        CString GetFileDescription() const
        {
            return msFileDescription;
        }
	/**
	 * @brief ȡļ汾
	 * @return  ַFileVersion
	 */
        CString GetFileVersion() const
        {
            return msFileVersion;
        }
	/**
	 * @brief ȡInternalName
	 * @return  ַInternalName
	 */
        CString GetInternalName() const
        {
            return msInternalName;
        }
	/**
	 * @brief ȡLegalCopyright
	 * @return  ַLegalCopyright
	 */
        CString GetLegalCopyright() const
        {
            return msLegalCopyright;
        }
	/**
	 * @brief ȡOriginalFileName
	 * @return  ַOriginalFileName
	 */
        CString GetOriginalFileName() const
        {
            return msOriginalFileName;
        }
	/**
	 * @brief ȡProductName
	 * @return  ַProductName
	 */
        CString GetProductName() const
        {
            return msProductName;
        }
	/**
	 * @brief ȡProductVersion
	 * @return  ַProductVersion
	 */
        CString GetProductVersion() const
        {
            return msProductVersion;
        }
	/**
	 * @brief ȡComments
	 * @return  ַComments
	 */
        CString GetComments() const
        {
            return msComments;
        }
	/**
	 * @brief ȡLegalTrademarks
	 * @return  ַLegalTrademarks
	 */
        CString GetLegalTrademarks() const
        {
            return msLegalTrademarks;
        }
	/**
	 * @brief ȡPrivateBuild
	 * @return  ַPrivateBuild
	 */
        CString GetPrivateBuild() const
        {
            return msPrivateBuild;
        }
	/**
	 * @brief ȡSpecialBuild
	 * @return  ַSpecialBuild
	 */
        CString GetSpecialBuild() const
        {
            return msSpecialBuild;
        }

    protected:
        virtual void Reset()
        {
            ZeroMemory(&mFileInfo, sizeof(mFileInfo));

            msCompanyName.Empty();
            msFileDescription.Empty();
            msFileVersion.Empty();
            msInternalName.Empty();
            msLegalCopyright.Empty();
            msOriginalFileName.Empty();
            msProductName.Empty();
            msProductVersion.Empty();
            msComments.Empty();
            msLegalTrademarks.Empty();
            msPrivateBuild.Empty();
            msSpecialBuild.Empty();
        }
	/**
	 * @brief ȡǰûıԱʾ
	 * @param[in]   lpData Bufferָļİ汾Ϣ
	 * @param[in]   unBlockSize VS_FIXEDFILEINFOṹĴС
	 * @param[in]  wLangId Աʾ
	 * @param[out]  dwId Աʾ
	 * @param[in]  bPrimaryEnough Ƿеֽڲ
	 * @return ɹTRUEʧܷFALSE
	 */
        BOOL GetTranslationId(
            LPVOID lpData,
            UINT   unBlockSize,
            WORD   wLangId,
            DWORD  &dwId,
            BOOL   bPrimaryEnough = FALSE)
        {
            LPWORD lpwData;

            for (lpwData = (LPWORD)lpData; (LPBYTE)lpwData < ((LPBYTE)lpData)+unBlockSize; lpwData+=2)
            {
                if (*lpwData == wLangId)
                {
                    dwId = *((DWORD*)lpwData);
                    return TRUE;
                }
            }

            if (!bPrimaryEnough)
                return FALSE;

            for (lpwData = (LPWORD)lpData; (LPBYTE)lpwData < ((LPBYTE)lpData)+unBlockSize; lpwData+=2)
            {
                if (((*lpwData)&0x00FF) == (wLangId&0x00FF))
                {
                    dwId = *((DWORD*)lpwData);
                    return TRUE;
                }
            }

            return FALSE;
        }

    private:
        VS_FIXEDFILEINFO mFileInfo;

        CString msCompanyName;
        CString msFileDescription;
        CString msFileVersion;
        CString msInternalName;
        CString msLegalCopyright;
        CString msOriginalFileName;
        CString msProductName;
        CString msProductVersion;
        CString msComments;
        CString msLegalTrademarks;
        CString msPrivateBuild;
        CString msSpecialBuild;
    };

}
}
<|endoftext|>


<commit_before>#include "stdafx.h"

typedef std::vector<DWORD> ThreadsVec;
ThreadsVec g_Threads;

unsigned g_EventsCount = 0;
unsigned g_ThreadEventsCount = 0;
unsigned g_CSwitchEventsCount = 0;
unsigned g_ThisProcessThreadEvents = 0;

static const wchar_t* THREAD_V2_CLSID(L"{3d6fa8d1-fe05-11d0-9dda-00c04fd7ba7c}");
static const unsigned CSWITCH_EVENT_TYPE = 36;

template<typename T>
struct default_release
{
	void operator()(T* ptr) const {
		ptr->Release();
	}
};

std::unique_ptr<IWbemClassObject, default_release<IWbemClassObject>> g_EventCategoryClass;

bool ListThreads(DWORD procId, ThreadsVec& outThreads) {
	HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
	THREADENTRY32 te32 = {0};

	// Take a snapshot of all running threads
	hThreadSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
	if(hThreadSnap == INVALID_HANDLE_VALUE) {
		return false;
	}

	te32.dwSize = sizeof(THREADENTRY32);

	if(!Thread32First(hThreadSnap, &te32))
	{
		std::cerr << "Unable to retrieve threads" << std::endl;
		::CloseHandle(hThreadSnap);
		return false;
	}

	// Now walk the thread list of the system,
	// and display information about each thread
	// associated with the specified process
	do
	{
		if(te32.th32OwnerProcessID == procId) {
			outThreads.push_back(te32.th32ThreadID);
		}
	} while(Thread32Next(hThreadSnap, &te32 ));

	::CloseHandle(hThreadSnap);
	return true;;
}

void WINAPI ProcessEvent(PEVENT_TRACE event) {
	++g_EventsCount;

	if(event->MofLength == 0)
		return;

	wchar_t ClassGUID[50] = {0};
	StringFromGUID2(event->Header.Guid, ClassGUID, sizeof(ClassGUID));

	// we are interested in Thread_V2 class events
	if(lstrcmpiW(ClassGUID, THREAD_V2_CLSID) == 0 && event->Header.Class.Version)
	{
		++g_ThreadEventsCount;

		if(event->Header.Class.Type == CSWITCH_EVENT_TYPE)
		{
			++g_CSwitchEventsCount;

		}
	}
}

void StartTraceConsume(TRACEHANDLE& handle) {
	::ProcessTrace(&handle, 1, 0, 0);
}

bool GetEventClass()
{
    HRESULT hr = S_OK;

    hr = CoInitialize(0);

	IWbemLocator* locatorTmp = nullptr;
    hr = CoCreateInstance(__uuidof(WbemLocator),
        0,
        CLSCTX_INPROC_SERVER,
        __uuidof(IWbemLocator),
        (LPVOID*)&locatorTmp);
	std::unique_ptr<IWbemLocator, default_release<IWbemLocator>> pLocator(locatorTmp);

    if (FAILED(hr)) {
        std::cerr << "CoCreateInstance failed with " << hr <<std::endl;
        return false;
    }

	IWbemServices* servicesTmp = nullptr;
	hr = pLocator->ConnectServer(L"root\\wmi",
        NULL, NULL, NULL,
        0L, NULL, NULL,
        &servicesTmp);
	std::unique_ptr<IWbemServices, default_release<IWbemServices>> service(servicesTmp);

    if (FAILED(hr)) {
        std::cerr << "ConnectServer failed with " << hr <<std::endl;
        return false;
    }

    hr = CoSetProxyBlanket(service.get(),
        RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
        NULL,
        RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL, EOAC_NONE);

    if (FAILED(hr)) {
        std::cerr << "CoSetProxyBlanket failed with " << hr << std::endl;
		return false;
    }

	// Get the class category
	IEnumWbemClassObject* classesTmp = nullptr;
	hr = service->CreateClassEnum(_bstr_t(L"EventTrace"),
        WBEM_FLAG_DEEP | WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_USE_AMENDED_QUALIFIERS,
        NULL, &classesTmp);
	std::unique_ptr<IEnumWbemClassObject, default_release<IEnumWbemClassObject>> classes(classesTmp);

	if(FAILED(hr)) {
		std::cerr << "CreateClassEnum failed with " << hr << std::endl;
		return false;
	}

	std::unique_ptr<IWbemClassObject, default_release<IWbemClassObject>> categoryClassObject;

	while (S_OK == hr) {
		ULONG cnt = 0;
		IWbemClassObject* clsTmp = nullptr;
		auto hrqual = classes->Next(WBEM_INFINITE, 1, &clsTmp, &cnt);
		std::unique_ptr<IWbemClassObject, default_release<IWbemClassObject>> cls(clsTmp);

		if(FAILED(hrqual)) {
			std::cerr << "Next failed with " << hrqual << std::endl;
			return false;
		}

		IWbemQualifierSet* qualTmp = nullptr;
		cls->GetQualifierSet(&qualTmp);
		std::unique_ptr<IWbemQualifierSet, default_release<IWbemQualifierSet>> qualifiers(qualTmp);
		if(qualifiers) {
			VARIANT varGuid;
			hrqual = qualifiers->Get(L"Guid", 0, &varGuid, nullptr);
			if(FAILED(hrqual)) {
				continue;
			}

			if(lstrcmpiW(varGuid.bstrVal, THREAD_V2_CLSID) == 0) {
				categoryClassObject.reset(cls.release());
				break;
			}
		}
	}

	// Get the class we are interested in from the category

	return true;
}

int main(int argc, char* argv[])
{
	if(!ListThreads(GetCurrentProcessId(), g_Threads)) {
		return 1;
	}

	std::cout << "Threads belonging to process: " << std::endl;
	std::for_each(g_Threads.cbegin(), g_Threads.cend(), [](DWORD threadId) {
		std::cout << threadId << std::endl;
	});

	// get the event class we are interested in
	if(!GetEventClass()) {
		return 1;
	}

	// Open the trace
	EVENT_TRACE_LOGFILE logDesc = {0};
	logDesc.LoggerName = KERNEL_LOGGER_NAME;
	logDesc.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME;
	logDesc.EventCallback = &ProcessEvent;

	TRACEHANDLE consumeHandle = ::OpenTrace(&logDesc);

	if(consumeHandle == INVALID_PROCESSTRACE_HANDLE) {
		std::cerr << "Unable to open trace!" << std::endl;
		return 1;
	}

	auto propsSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(KERNEL_LOGGER_NAME);
	std::unique_ptr<unsigned char[]> evtPropertiesMem(new unsigned char[propsSize]);
	::memset(evtPropertiesMem.get(), 0, propsSize);
	PEVENT_TRACE_PROPERTIES properties = reinterpret_cast<PEVENT_TRACE_PROPERTIES>(evtPropertiesMem.get());
	properties->Wnode.BufferSize = propsSize;
	properties->Wnode.Guid = SystemTraceControlGuid;
	properties->Wnode.ClientContext = 1/*QPC*/;
	properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
	properties->EnableFlags = EVENT_TRACE_FLAG_CSWITCH;
	properties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
	properties->LogFileNameOffset = 0;
	properties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);

	TRACEHANDLE session;
	auto error = ::StartTrace(&session, KERNEL_LOGGER_NAME, properties);
	if(error != ERROR_SUCCESS) {
		std::cerr << "Unable to start trace; error: " << error << std::endl;
	}
	std::thread consumerThread(std::bind(&StartTraceConsume, consumeHandle));

	::Sleep(1);

	// Stop the session
	properties->LoggerNameOffset = 0;
	error = ::ControlTrace(session, KERNEL_LOGGER_NAME, properties, EVENT_TRACE_CONTROL_STOP);

	if(error != ERROR_SUCCESS) {
		std::cerr << "Unable to stop trace; error: " << error << std::endl;
	}

	std::cout << "Free buffers: " << properties->FreeBuffers << std::endl;
	std::cout << "Events lost: " << properties->EventsLost << std::endl;
	std::cout << "Buffers written: " << properties->BuffersWritten << std::endl;
	std::cout << "Log buffers lost: " << properties->LogBuffersLost << std::endl;
	std::cout << "RealTime buffers lost: " << properties->RealTimeBuffersLost << std::endl;

	error = ::CloseTrace(consumeHandle);
	if(error != ERROR_SUCCESS) {
		std::cerr << "Unable to close consume trace; error: " << error << std::endl;
	}

	consumerThread.join();
	std::cout << "Events received: " << g_EventsCount << std::endl;
	std::cout << "Thread events received: " << g_ThreadEventsCount << std::endl;
	std::cout << "CSwitch events received: " << g_CSwitchEventsCount << std::endl;

	return 0;
}

<commit_msg>* Fetched the event class for the CSwitch events we are interested in<commit_after>#include "stdafx.h"

typedef std::vector<DWORD> ThreadsVec;
ThreadsVec g_Threads;

unsigned g_EventsCount = 0;
unsigned g_ThreadEventsCount = 0;
unsigned g_CSwitchEventsCount = 0;
unsigned g_ThisProcessThreadEvents = 0;

static const wchar_t* THREAD_V2_CLSID(L"{3d6fa8d1-fe05-11d0-9dda-00c04fd7ba7c}");
static const unsigned CSWITCH_EVENT_TYPE = 36;

template<typename T>
struct default_release
{
	void operator()(T* ptr) const {
		ptr->Release();
	}
};

struct free_variant
{
	void operator()(VARIANT* ptr) const {
		VariantClear(ptr);
	}
};

std::unique_ptr<IWbemClassObject, default_release<IWbemClassObject>> g_EventCategoryClass;

bool ListThreads(DWORD procId, ThreadsVec& outThreads) {
	HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
	THREADENTRY32 te32 = {0};

	// Take a snapshot of all running threads
	hThreadSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
	if(hThreadSnap == INVALID_HANDLE_VALUE) {
		return false;
	}

	te32.dwSize = sizeof(THREADENTRY32);

	if(!Thread32First(hThreadSnap, &te32))
	{
		std::cerr << "Unable to retrieve threads" << std::endl;
		::CloseHandle(hThreadSnap);
		return false;
	}

	// Now walk the thread list of the system,
	// and display information about each thread
	// associated with the specified process
	do
	{
		if(te32.th32OwnerProcessID == procId) {
			outThreads.push_back(te32.th32ThreadID);
		}
	} while(Thread32Next(hThreadSnap, &te32 ));

	::CloseHandle(hThreadSnap);
	return true;;
}

void WINAPI ProcessEvent(PEVENT_TRACE event) {
	++g_EventsCount;

	if(event->MofLength == 0)
		return;

	wchar_t ClassGUID[50] = {0};
	StringFromGUID2(event->Header.Guid, ClassGUID, sizeof(ClassGUID));

	// we are interested in Thread_V2 class events
	if(lstrcmpiW(ClassGUID, THREAD_V2_CLSID) == 0 && event->Header.Class.Version)
	{
		++g_ThreadEventsCount;

		if(event->Header.Class.Type == CSWITCH_EVENT_TYPE)
		{
			++g_CSwitchEventsCount;

		}
	}
}

void StartTraceConsume(TRACEHANDLE& handle) {
	::ProcessTrace(&handle, 1, 0, 0);
}

bool GetEventClass()
{
    HRESULT hr = S_OK;

    hr = CoInitialize(0);

	IWbemLocator* locatorTmp = nullptr;
    hr = CoCreateInstance(__uuidof(WbemLocator),
        0,
        CLSCTX_INPROC_SERVER,
        __uuidof(IWbemLocator),
        (LPVOID*)&locatorTmp);
	std::unique_ptr<IWbemLocator, default_release<IWbemLocator>> pLocator(locatorTmp);

    if (FAILED(hr)) {
        std::cerr << "CoCreateInstance failed with " << hr <<std::endl;
        return false;
    }

	IWbemServices* servicesTmp = nullptr;
	hr = pLocator->ConnectServer(L"root\\wmi",
        NULL, NULL, NULL,
        0L, NULL, NULL,
        &servicesTmp);
	std::unique_ptr<IWbemServices, default_release<IWbemServices>> service(servicesTmp);

    if (FAILED(hr)) {
        std::cerr << "ConnectServer failed with " << hr <<std::endl;
        return false;
    }

    hr = CoSetProxyBlanket(service.get(),
        RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
        NULL,
        RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL, EOAC_NONE);

    if (FAILED(hr)) {
        std::cerr << "CoSetProxyBlanket failed with " << hr << std::endl;
		return false;
    }

	// Get the class category
	IEnumWbemClassObject* classesTmp = nullptr;
	hr = service->CreateClassEnum(_bstr_t(L"EventTrace"),
        WBEM_FLAG_DEEP | WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_USE_AMENDED_QUALIFIERS,
        NULL, &classesTmp);
	std::unique_ptr<IEnumWbemClassObject, default_release<IEnumWbemClassObject>> classes(classesTmp);

	if(FAILED(hr)) {
		std::cerr << "CreateClassEnum failed with " << hr << std::endl;
		return false;
	}

	std::unique_ptr<IWbemClassObject, default_release<IWbemClassObject>> categoryClassObject;

	while (S_OK == hr) {
		ULONG cnt = 0;
		IWbemClassObject* clsTmp = nullptr;
		auto hrqual = classes->Next(WBEM_INFINITE, 1, &clsTmp, &cnt);
		std::unique_ptr<IWbemClassObject, default_release<IWbemClassObject>> cls(clsTmp);

		if(FAILED(hrqual)) {
			std::cerr << "Next category failed with " << hrqual << std::endl;
			return false;
		}

		IWbemQualifierSet* qualTmp = nullptr;
		cls->GetQualifierSet(&qualTmp);
		std::unique_ptr<IWbemQualifierSet, default_release<IWbemQualifierSet>> qualifiers(qualTmp);
		if(qualifiers) {
			std::unique_ptr<VARIANT, free_variant> varGuid(new VARIANT);
			hrqual = qualifiers->Get(L"Guid", 0, varGuid.get(), nullptr);
			if(FAILED(hrqual)) {
				continue;
			}

			if(lstrcmpiW(varGuid->bstrVal, THREAD_V2_CLSID) == 0) {
				categoryClassObject.reset(cls.release());
				break;
			}
		}
	}

	std::unique_ptr<VARIANT, free_variant> varClassName(new VARIANT);
	// Get the class we are interested in from the category
    hr = categoryClassObject->Get(L"__RELPATH", 0, varClassName.get(), nullptr, nullptr);
	if(FAILED(hr)) {
		std::cerr << "Unable to get relpath " << hr << std::endl;
		return false;
	}

	hr = service->CreateClassEnum(varClassName->bstrVal,
        WBEM_FLAG_SHALLOW | WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_USE_AMENDED_QUALIFIERS,
        NULL, &classesTmp);
	classes.reset(classesTmp);
	if(FAILED(hr)) {
		std::cerr << "Unable to get classes for category " << hr << std::endl;
		return false;
	}

	bool found = false;
	std::unique_ptr<IWbemClassObject, default_release<IWbemClassObject>> cls;
	while(S_OK == hr) {
		ULONG cnt = 0;
		IWbemClassObject* clsTmp = nullptr;
		auto hrqual = classes->Next(WBEM_INFINITE, 1, &clsTmp, &cnt);
		cls.reset(clsTmp);

		if(FAILED(hrqual)) {
			std::cerr << "Next class failed with " << hrqual << std::endl;
			return false;
		}

		IWbemQualifierSet* qualTmp = nullptr;
		cls->GetQualifierSet(&qualTmp);
		std::unique_ptr<IWbemQualifierSet, default_release<IWbemQualifierSet>> qualifiers(qualTmp);

		std::unique_ptr<VARIANT, free_variant> varEventType(new VARIANT);
		qualifiers->Get(L"EventType", 0, varEventType.get(), nullptr);

		if (varEventType->vt & VT_ARRAY) {
            HRESULT hrSafe = S_OK;
            int ClassEventType;
            SAFEARRAY* pEventTypes = varEventType->parray;

            for (LONG i=0; (ULONG)i < pEventTypes->rgsabound->cElements; i++) {
                hrSafe = SafeArrayGetElement(pEventTypes, &i, &ClassEventType);

                if (ClassEventType == CSWITCH_EVENT_TYPE) {
                    found = true;
                    break;  //for loop
                }
            }
        }
		else {
            if (varEventType->intVal == CSWITCH_EVENT_TYPE) {
                found = true;
            }
        }
		if (found) {
            break;  //while loop
        }
	}

	if(found) {
		g_EventCategoryClass = std::move(cls);
	}

	return found;
}

int main(int argc, char* argv[])
{
	if(!ListThreads(GetCurrentProcessId(), g_Threads)) {
		return 1;
	}

	std::cout << "Threads belonging to process: " << std::endl;
	std::for_each(g_Threads.cbegin(), g_Threads.cend(), [](DWORD threadId) {
		std::cout << threadId << std::endl;
	});

	// get the event class we are interested in
	if(!GetEventClass()) {
		return 1;
	}

	// Open the trace
	EVENT_TRACE_LOGFILE logDesc = {0};
	logDesc.LoggerName = KERNEL_LOGGER_NAME;
	logDesc.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME;
	logDesc.EventCallback = &ProcessEvent;

	TRACEHANDLE consumeHandle = ::OpenTrace(&logDesc);

	if(consumeHandle == INVALID_PROCESSTRACE_HANDLE) {
		std::cerr << "Unable to open trace!" << std::endl;
		return 1;
	}

	auto propsSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(KERNEL_LOGGER_NAME);
	std::unique_ptr<unsigned char[]> evtPropertiesMem(new unsigned char[propsSize]);
	::memset(evtPropertiesMem.get(), 0, propsSize);
	PEVENT_TRACE_PROPERTIES properties = reinterpret_cast<PEVENT_TRACE_PROPERTIES>(evtPropertiesMem.get());
	properties->Wnode.BufferSize = propsSize;
	properties->Wnode.Guid = SystemTraceControlGuid;
	properties->Wnode.ClientContext = 1/*QPC*/;
	properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
	properties->EnableFlags = EVENT_TRACE_FLAG_CSWITCH;
	properties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
	properties->LogFileNameOffset = 0;
	properties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);

	TRACEHANDLE session;
	auto error = ::StartTrace(&session, KERNEL_LOGGER_NAME, properties);
	if(error != ERROR_SUCCESS) {
		std::cerr << "Unable to start trace; error: " << error << std::endl;
	}
	std::thread consumerThread(std::bind(&StartTraceConsume, consumeHandle));

	::Sleep(1);

	// Stop the session
	properties->LoggerNameOffset = 0;
	error = ::ControlTrace(session, KERNEL_LOGGER_NAME, properties, EVENT_TRACE_CONTROL_STOP);

	if(error != ERROR_SUCCESS) {
		std::cerr << "Unable to stop trace; error: " << error << std::endl;
	}

	std::cout << "Free buffers: " << properties->FreeBuffers << std::endl;
	std::cout << "Events lost: " << properties->EventsLost << std::endl;
	std::cout << "Buffers written: " << properties->BuffersWritten << std::endl;
	std::cout << "Log buffers lost: " << properties->LogBuffersLost << std::endl;
	std::cout << "RealTime buffers lost: " << properties->RealTimeBuffersLost << std::endl;

	error = ::CloseTrace(consumeHandle);
	if(error != ERROR_SUCCESS) {
		std::cerr << "Unable to close consume trace; error: " << error << std::endl;
	}

	consumerThread.join();
	std::cout << "Events received: " << g_EventsCount << std::endl;
	std::cout << "Thread events received: " << g_ThreadEventsCount << std::endl;
	std::cout << "CSwitch events received: " << g_CSwitchEventsCount << std::endl;

	return 0;
}

<|endoftext|>


<commit_before><commit_msg>This fixes (again) the rendering problem we have with zero-height window resizes from Firefox.  It puts back the explicit window resize, but doesn't do it if the window is zero width or height.  This seems to fix the problem without causing resize problems in the beach demo.<commit_after><|endoftext|>


<commit_before>/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "Test.h"

#include "Resources.h"
#include "SkCanvas.h"
#include "SkDocument.h"
#include "SkOSFile.h"
#include "SkOSPath.h"
#include "SkStream.h"
#include "SkPixelSerializer.h"

static void test_empty(skiatest::Reporter* reporter) {
    SkDynamicMemoryWStream stream;

    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));

    doc->close();

    REPORTER_ASSERT(reporter, stream.bytesWritten() == 0);
}

static void test_abort(skiatest::Reporter* reporter) {
    SkDynamicMemoryWStream stream;
    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));

    SkCanvas* canvas = doc->beginPage(100, 100);
    canvas->drawColor(SK_ColorRED);
    doc->endPage();

    doc->abort();

    // Test that only the header is written, not the full document.
    REPORTER_ASSERT(reporter, stream.bytesWritten() < 256);
}

static void test_abortWithFile(skiatest::Reporter* reporter) {
    SkString tmpDir = skiatest::GetTmpDir();

    if (tmpDir.isEmpty()) {
        return;  // TODO(edisonn): unfortunatelly this pattern is used in other
                 // tests, but if GetTmpDir() starts returning and empty dir
                 // allways, then all these tests will be disabled.
    }

    SkString path = SkOSPath::Join(tmpDir.c_str(), "aborted.pdf");

    // Make sure doc's destructor is called to flush.
    {
        sk_sp<SkDocument> doc(SkDocument::MakePDF(path.c_str()));

        SkCanvas* canvas = doc->beginPage(100, 100);
        canvas->drawColor(SK_ColorRED);
        doc->endPage();

        doc->abort();
    }

    FILE* file = fopen(path.c_str(), "r");
    // The created file should be empty.
    char buffer[100];
    REPORTER_ASSERT(reporter, fread(buffer, 1, 1, file) == 0);
    fclose(file);
}

static void test_file(skiatest::Reporter* reporter) {
    SkString tmpDir = skiatest::GetTmpDir();
    if (tmpDir.isEmpty()) {
        return;  // TODO(edisonn): unfortunatelly this pattern is used in other
                 // tests, but if GetTmpDir() starts returning and empty dir
                 // allways, then all these tests will be disabled.
    }

    SkString path = SkOSPath::Join(tmpDir.c_str(), "file.pdf");

    sk_sp<SkDocument> doc(SkDocument::MakePDF(path.c_str()));

    SkCanvas* canvas = doc->beginPage(100, 100);

    canvas->drawColor(SK_ColorRED);
    doc->endPage();
    doc->close();

    FILE* file = fopen(path.c_str(), "r");
    REPORTER_ASSERT(reporter, file != nullptr);
    char header[100];
    REPORTER_ASSERT(reporter, fread(header, 4, 1, file) != 0);
    REPORTER_ASSERT(reporter, strncmp(header, "%PDF", 4) == 0);
    fclose(file);
}

static void test_close(skiatest::Reporter* reporter) {
    SkDynamicMemoryWStream stream;
    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));

    SkCanvas* canvas = doc->beginPage(100, 100);
    canvas->drawColor(SK_ColorRED);
    doc->endPage();

    doc->close();

    REPORTER_ASSERT(reporter, stream.bytesWritten() != 0);
}

DEF_TEST(SkPDF_document_tests, reporter) {
    REQUIRE_PDF_DOCUMENT(document_tests, reporter);
    test_empty(reporter);
    test_abort(reporter);
    test_abortWithFile(reporter);
    test_file(reporter);
    test_close(reporter);
}

namespace {
class JPEGSerializer final : public SkPixelSerializer {
    bool onUseEncodedData(const void*, size_t) override { return true; }
    SkData* onEncode(const SkPixmap& pixmap) override {
        SkBitmap bm;
        return bm.installPixels(pixmap.info(),
                                pixmap.writable_addr(),
                                pixmap.rowBytes(),
                                pixmap.ctable(),
                                nullptr, nullptr)
            ? SkImageEncoder::EncodeData(bm, SkImageEncoder::kJPEG_Type, 85)
            : nullptr;
    }
};
}  // namespace

size_t count_bytes(const SkBitmap& bm, bool useDCT) {
    SkDynamicMemoryWStream stream;
    sk_sp<SkDocument> doc;
    if (useDCT) {
        doc = SkDocument::MakePDF(&stream, SK_ScalarDefaultRasterDPI,
                                  SkDocument::PDFMetadata(),
                                  sk_make_sp<JPEGSerializer>(), false);
    } else {
        doc = SkDocument::MakePDF(&stream);
    }
    SkCanvas* canvas = doc->beginPage(64, 64);
    canvas->drawBitmap(bm, 0, 0);
    doc->endPage();
    doc->close();
    return stream.bytesWritten();
}

DEF_TEST(SkPDF_document_dct_encoder, r) {
    REQUIRE_PDF_DOCUMENT(SkPDF_document_dct_encoder, r);
    SkBitmap bm;
    if (GetResourceAsBitmap("mandrill_64.png", &bm)) {
        // Lossy encoding works better on photographs.
        REPORTER_ASSERT(r, count_bytes(bm, true) < count_bytes(bm, false));
    }
}

DEF_TEST(SkPDF_document_skbug_4734, r) {
    REQUIRE_PDF_DOCUMENT(SkPDF_document_skbug_4734, r);
    SkDynamicMemoryWStream stream;
    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
    SkCanvas* canvas = doc->beginPage(64, 64);
    canvas->scale(10000.0f, 10000.0f);
    canvas->translate(20.0f, 10.0f);
    canvas->rotate(30.0f);
    const char text[] = "HELLO";
    canvas->drawText(text, strlen(text), 0, 0, SkPaint());
}

static bool contains(const uint8_t* result, size_t size, const char expectation[]) {
    size_t len = strlen(expectation);
    size_t N = 1 + size - len;
    for (size_t i = 0; i < N; ++i) {
        if (0 == memcmp(result + i, expectation, len)) {
            return true;
        }
    }
    return false;
}

// verify that the PDFA flag does something.
DEF_TEST(SkPDF_pdfa_document, r) {
    REQUIRE_PDF_DOCUMENT(SkPDF_pdfa_document, r);

    SkDocument::PDFMetadata pdfMetadata;
    pdfMetadata.fTitle = "test document";
    pdfMetadata.fCreation.fEnabled = true;
    pdfMetadata.fCreation.fDateTime = {0, 1999, 12, 5, 31, 23, 59, 59};

    SkDynamicMemoryWStream buffer;
    auto doc = SkDocument::MakePDF(&buffer, SK_ScalarDefaultRasterDPI,
                                   pdfMetadata, nullptr, /* pdfa = */ true);
    doc->beginPage(64, 64)->drawColor(SK_ColorRED);
    doc->close();
    sk_sp<SkData> data(buffer.detachAsData());

    static const char* expectations[] = {
        "sRGB IEC61966-2.1",
        "<dc:title><rdf:Alt><rdf:li xml:lang=\"x-default\">test document",
        "<xmp:CreateDate>1999-12-31T23:59:59+00:00</xmp:CreateDate>",
        "/Subtype /XML",
        "/CreationDate (D:19991231235959+00'00')>>",
    };
    for (const char* expectation : expectations) {
        if (!contains(data->bytes(), data->size(), expectation)) {
            ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
        }
    }
    pdfMetadata.fProducer = "phoney library";
    doc = SkDocument::MakePDF(&buffer, SK_ScalarDefaultRasterDPI,
                              pdfMetadata, nullptr, /* pdfa = */ true);
    doc->beginPage(64, 64)->drawColor(SK_ColorRED);
    doc->close();
    data = buffer.detachAsData();

    static const char* moreExpectations[] = {
        "/Producer (phoney library)",
        "/ProductionLibrary (Skia/PDF m",
        "<!-- <skia:ProductionLibrary>Skia/PDF m",
        "<pdf:Producer>phoney library</pdf:Producer>",
    };
    for (const char* expectation : moreExpectations) {
        if (!contains(data->bytes(), data->size(), expectation)) {
            ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
        }
    }
}
<commit_msg>Fix BUG=skia:5662<commit_after>/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "Test.h"

#include "Resources.h"
#include "SkCanvas.h"
#include "SkDocument.h"
#include "SkOSFile.h"
#include "SkOSPath.h"
#include "SkStream.h"
#include "SkPixelSerializer.h"

static void test_empty(skiatest::Reporter* reporter) {
    SkDynamicMemoryWStream stream;

    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));

    doc->close();

    REPORTER_ASSERT(reporter, stream.bytesWritten() == 0);
}

static void test_abort(skiatest::Reporter* reporter) {
    SkDynamicMemoryWStream stream;
    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));

    SkCanvas* canvas = doc->beginPage(100, 100);
    canvas->drawColor(SK_ColorRED);
    doc->endPage();

    doc->abort();

    // Test that only the header is written, not the full document.
    REPORTER_ASSERT(reporter, stream.bytesWritten() < 256);
}

static void test_abortWithFile(skiatest::Reporter* reporter) {
    SkString tmpDir = skiatest::GetTmpDir();

    if (tmpDir.isEmpty()) {
        return;  // TODO(edisonn): unfortunatelly this pattern is used in other
                 // tests, but if GetTmpDir() starts returning and empty dir
                 // allways, then all these tests will be disabled.
    }

    SkString path = SkOSPath::Join(tmpDir.c_str(), "aborted.pdf");

    // Make sure doc's destructor is called to flush.
    {
        sk_sp<SkDocument> doc(SkDocument::MakePDF(path.c_str()));

        SkCanvas* canvas = doc->beginPage(100, 100);
        canvas->drawColor(SK_ColorRED);
        doc->endPage();

        doc->abort();
    }

    FILE* file = fopen(path.c_str(), "r");
    // Test that only the header is written, not the full document.
    char buffer[256];
    REPORTER_ASSERT(reporter, fread(buffer, 1, sizeof(buffer), file) < sizeof(buffer));
    fclose(file);
}

static void test_file(skiatest::Reporter* reporter) {
    SkString tmpDir = skiatest::GetTmpDir();
    if (tmpDir.isEmpty()) {
        return;  // TODO(edisonn): unfortunatelly this pattern is used in other
                 // tests, but if GetTmpDir() starts returning and empty dir
                 // allways, then all these tests will be disabled.
    }

    SkString path = SkOSPath::Join(tmpDir.c_str(), "file.pdf");

    sk_sp<SkDocument> doc(SkDocument::MakePDF(path.c_str()));

    SkCanvas* canvas = doc->beginPage(100, 100);

    canvas->drawColor(SK_ColorRED);
    doc->endPage();
    doc->close();

    FILE* file = fopen(path.c_str(), "r");
    REPORTER_ASSERT(reporter, file != nullptr);
    char header[100];
    REPORTER_ASSERT(reporter, fread(header, 4, 1, file) != 0);
    REPORTER_ASSERT(reporter, strncmp(header, "%PDF", 4) == 0);
    fclose(file);
}

static void test_close(skiatest::Reporter* reporter) {
    SkDynamicMemoryWStream stream;
    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));

    SkCanvas* canvas = doc->beginPage(100, 100);
    canvas->drawColor(SK_ColorRED);
    doc->endPage();

    doc->close();

    REPORTER_ASSERT(reporter, stream.bytesWritten() != 0);
}

DEF_TEST(SkPDF_document_tests, reporter) {
    REQUIRE_PDF_DOCUMENT(document_tests, reporter);
    test_empty(reporter);
    test_abort(reporter);
    test_abortWithFile(reporter);
    test_file(reporter);
    test_close(reporter);
}

namespace {
class JPEGSerializer final : public SkPixelSerializer {
    bool onUseEncodedData(const void*, size_t) override { return true; }
    SkData* onEncode(const SkPixmap& pixmap) override {
        SkBitmap bm;
        return bm.installPixels(pixmap.info(),
                                pixmap.writable_addr(),
                                pixmap.rowBytes(),
                                pixmap.ctable(),
                                nullptr, nullptr)
            ? SkImageEncoder::EncodeData(bm, SkImageEncoder::kJPEG_Type, 85)
            : nullptr;
    }
};
}  // namespace

size_t count_bytes(const SkBitmap& bm, bool useDCT) {
    SkDynamicMemoryWStream stream;
    sk_sp<SkDocument> doc;
    if (useDCT) {
        doc = SkDocument::MakePDF(&stream, SK_ScalarDefaultRasterDPI,
                                  SkDocument::PDFMetadata(),
                                  sk_make_sp<JPEGSerializer>(), false);
    } else {
        doc = SkDocument::MakePDF(&stream);
    }
    SkCanvas* canvas = doc->beginPage(64, 64);
    canvas->drawBitmap(bm, 0, 0);
    doc->endPage();
    doc->close();
    return stream.bytesWritten();
}

DEF_TEST(SkPDF_document_dct_encoder, r) {
    REQUIRE_PDF_DOCUMENT(SkPDF_document_dct_encoder, r);
    SkBitmap bm;
    if (GetResourceAsBitmap("mandrill_64.png", &bm)) {
        // Lossy encoding works better on photographs.
        REPORTER_ASSERT(r, count_bytes(bm, true) < count_bytes(bm, false));
    }
}

DEF_TEST(SkPDF_document_skbug_4734, r) {
    REQUIRE_PDF_DOCUMENT(SkPDF_document_skbug_4734, r);
    SkDynamicMemoryWStream stream;
    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
    SkCanvas* canvas = doc->beginPage(64, 64);
    canvas->scale(10000.0f, 10000.0f);
    canvas->translate(20.0f, 10.0f);
    canvas->rotate(30.0f);
    const char text[] = "HELLO";
    canvas->drawText(text, strlen(text), 0, 0, SkPaint());
}

static bool contains(const uint8_t* result, size_t size, const char expectation[]) {
    size_t len = strlen(expectation);
    size_t N = 1 + size - len;
    for (size_t i = 0; i < N; ++i) {
        if (0 == memcmp(result + i, expectation, len)) {
            return true;
        }
    }
    return false;
}

// verify that the PDFA flag does something.
DEF_TEST(SkPDF_pdfa_document, r) {
    REQUIRE_PDF_DOCUMENT(SkPDF_pdfa_document, r);

    SkDocument::PDFMetadata pdfMetadata;
    pdfMetadata.fTitle = "test document";
    pdfMetadata.fCreation.fEnabled = true;
    pdfMetadata.fCreation.fDateTime = {0, 1999, 12, 5, 31, 23, 59, 59};

    SkDynamicMemoryWStream buffer;
    auto doc = SkDocument::MakePDF(&buffer, SK_ScalarDefaultRasterDPI,
                                   pdfMetadata, nullptr, /* pdfa = */ true);
    doc->beginPage(64, 64)->drawColor(SK_ColorRED);
    doc->close();
    sk_sp<SkData> data(buffer.detachAsData());

    static const char* expectations[] = {
        "sRGB IEC61966-2.1",
        "<dc:title><rdf:Alt><rdf:li xml:lang=\"x-default\">test document",
        "<xmp:CreateDate>1999-12-31T23:59:59+00:00</xmp:CreateDate>",
        "/Subtype /XML",
        "/CreationDate (D:19991231235959+00'00')>>",
    };
    for (const char* expectation : expectations) {
        if (!contains(data->bytes(), data->size(), expectation)) {
            ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
        }
    }
    pdfMetadata.fProducer = "phoney library";
    doc = SkDocument::MakePDF(&buffer, SK_ScalarDefaultRasterDPI,
                              pdfMetadata, nullptr, /* pdfa = */ true);
    doc->beginPage(64, 64)->drawColor(SK_ColorRED);
    doc->close();
    data = buffer.detachAsData();

    static const char* moreExpectations[] = {
        "/Producer (phoney library)",
        "/ProductionLibrary (Skia/PDF m",
        "<!-- <skia:ProductionLibrary>Skia/PDF m",
        "<pdf:Producer>phoney library</pdf:Producer>",
    };
    for (const char* expectation : moreExpectations) {
        if (!contains(data->bytes(), data->size(), expectation)) {
            ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
        }
    }
}
<|endoftext|>


<commit_before>#if !defined(_P0_)

template <typename T, typename U> class P0 {
public:
  typedef SimpleVector<T> Vec;
  typedef SimpleVector<U> VecU;
  typedef SimpleMatrix<T> Mat;
  typedef SimpleMatrix<U> MatU;
  inline P0(const int& range, const int& shrink = 4);
  inline ~P0();
  inline void nextNoreturn(const T& in);
  inline T next(const T& in);
private:
  Vec pred;
  const MatU& seed(const int& size, const bool& idft);
  const Mat&  diff(const int& size, const bool& integrate);
  const Vec&  nextTaylor(const int& size);
  const Vec&  nextDeepTaylor(const int& size, const int& shrink);
  const T& Pi() const;
  const U& J()  const;
  Vec buf;
};

template <typename T, typename U> P0<T,U>::P0(const int& range, const int& shrink) {
  assert(1 < range && 0 < shrink);
  buf.resize(range);
  for(int i = 0; i < buf.size(); i ++)
    buf[i] = T(0);
  const auto& pred0(nextDeepTaylor(range, shrink));
  pred.resize(range);
  for(int i = 0; i < pred.size(); i ++)
    pred[i] = T(0);
  for(int i = 0; i < pred0.size(); i ++)
    pred[i / shrink] += pred0[i];
  pred /= T(shrink);
}

template <typename T, typename U> P0<T,U>::~P0() {
  ;
}

template <typename T, typename U> inline void P0<T,U>::nextNoreturn(const T& in) {
  for(int i = 1; i < buf.size(); i ++)
    buf[i - 1] = buf[i];
  buf[buf.size() - 1] = in;
  return;
}

template <typename T, typename U> inline T P0<T,U>::next(const T& in) {
  nextNoreturn(in);
  return pred.dot(buf);
}

template <typename T, typename U> const T& P0<T,U>::Pi() const {
  const static auto pi(atan2(T(1), T(1)) * T(4));
  return pi;
}

template <typename T, typename U> const U& P0<T,U>::J() const {
  const static auto i(sqrt(U(T(- 1))));
  return i;
}

template <typename T, typename U> const typename P0<T,U>::MatU& P0<T,U>::seed(const int& size, const bool& f_idft) {
  assert(0 < size);
  static vector<MatU> dft;
  static vector<MatU> idft;
  if(dft.size() <= size)
    dft.resize(size + 1, MatU());
  if(idft.size() <= size)
    idft.resize(size + 1, MatU());
  if((!f_idft) &&  dft[size].rows() == size &&  dft[size].cols() == size)
    return dft[size];
  if(  f_idft  && idft[size].rows() == size && idft[size].cols() == size)
    return idft[size];
  dft[ size].resize(size, size);
  idft[size].resize(size, size);
  auto& edft( dft[size]);
  auto& eidft(idft[size]);
  for(int i = 0; i < edft.rows(); i ++)
    for(int j = 0; j < edft.cols(); j ++) {
      const auto theta(- T(2) * Pi() * T(i) * T(j) / T(edft.rows()));
      edft(i, j)  = U(cos(  theta), sin(  theta));
      eidft(i, j) = U(cos(- theta), sin(- theta)) / U(T(size));
    }
  if(f_idft)
    return eidft;
  return edft;
}

template <typename T, typename U> const typename P0<T,U>::Mat& P0<T,U>::diff(const int& size, const bool& integrate) {
  assert(0 < size);
  static vector<Mat> D;
  static vector<Mat> I;
  if(D.size() <= size)
    D.resize(size + 1, Mat());
  if(I.size() <= size)
    I.resize(size + 1, Mat());
  if((!integrate) && D[size].rows() == size && D[size].cols() == size)
    return D[size];
  if(  integrate  && I[size].rows() == size && I[size].cols() == size)
    return I[size];
  D[size].resize(size, size);
  I[size].resize(size, size);
  auto& d(D[size]);
  auto& i(I[size]);
  auto  DD(seed(size, false));
  DD.row(0) *= U(T(0));
  auto  II(DD);
  T nd(0);
  T ni(0);
  for(int i = 1; i < DD.rows(); i ++) {
    const auto phase(- J() * T(2) * Pi() * T(i) / T(DD.rows()));
    const auto phase2(U(T(1)) / phase);
    DD.row(i) *= phase;
    II.row(i) /= phase;
    nd += abs(phase)  * abs(phase);
    ni += abs(phase2) * abs(phase2);
  }
  d = (seed(size, true) * DD).template real<T>() * sqrt(sqrt(T(DD.rows() - 1) / (nd * ni)));
  i = (seed(size, true) * II).template real<T>() * sqrt(sqrt(T(II.rows() - 1) / (nd * ni)));
  if(integrate)
    return i;
  return d;
}

template <typename T, typename U> const typename P0<T,U>::Vec& P0<T,U>::nextTaylor(const int& size) {
  assert(0 < size);
  static vector<Vec> ntayl;
  if(ntayl.size() <= size)
    ntayl.resize(size + 1, Vec());
  if(ntayl[size].size() == size)
    return ntayl[size];
  ntayl[size].resize(size);
        auto& v(ntayl[size]);
  const auto& D(diff(size, false));
        auto  dd(D.row(D.rows() - 1));
  for(int i = 0; i < v.size() - 1; i ++)
    v[i] = T(0);
  v[v.size() - 1] = T(1);
  for(int i = 2; 0 <= i; i ++) {
    const auto bv(v);
    v += dd;
    if(bv == v)
      break;
    dd = (D * dd) / T(i);
  }
  return v;
}

template <typename T, typename U> const typename P0<T,U>::Vec& P0<T,U>::nextDeepTaylor(const int& size, const int& shrink) {
  assert(0 < size);
  static vector<Vec> dtayl;
  if(dtayl.size() <= size)
    dtayl.resize(size + 1, Vec());
  if(dtayl[size].size() == size)
    return dtayl[size];
  auto& p(dtayl[size]);
  p = nextTaylor(size);
  for(int i = shrink + 1; i < p.size(); i ++) {
    const auto& q(nextTaylor(i));
    for(int j = 0; j < q.size(); j ++)
      p[p.size() + j - q.size()] += q[j];
  }
  return p /= T(p.size() - 1);
}

#define _P0_
#endif

<commit_msg>fix last.<commit_after>#if !defined(_P0_)

template <typename T, typename U> class P0 {
public:
  typedef SimpleVector<T> Vec;
  typedef SimpleVector<U> VecU;
  typedef SimpleMatrix<T> Mat;
  typedef SimpleMatrix<U> MatU;
  inline P0(const int& range, const int& shrink = 4);
  inline ~P0();
  inline void nextNoreturn(const T& in);
  inline T next(const T& in);
private:
  Vec pred;
  const MatU& seed(const int& size, const bool& idft);
  const Mat&  diff(const int& size, const bool& integrate);
  const Vec&  nextTaylor(const int& size);
  const Vec&  nextDeepTaylor(const int& size, const int& shrink);
  const T& Pi() const;
  const U& J()  const;
  Vec buf;
};

template <typename T, typename U> P0<T,U>::P0(const int& range, const int& shrink) {
  assert(1 < range && 0 < shrink);
  buf.resize(range);
  for(int i = 0; i < buf.size(); i ++)
    buf[i] = T(0);
  const auto& pred0(nextDeepTaylor(range * shrink, shrink));
  pred.resize(range);
  for(int i = 0; i < pred.size(); i ++)
    pred[i] = T(0);
  for(int i = 0; i < pred0.size(); i ++)
    pred[i / shrink] += pred0[i];
  pred /= T(shrink);
}

template <typename T, typename U> P0<T,U>::~P0() {
  ;
}

template <typename T, typename U> inline void P0<T,U>::nextNoreturn(const T& in) {
  for(int i = 1; i < buf.size(); i ++)
    buf[i - 1] = buf[i];
  buf[buf.size() - 1] = in;
  return;
}

template <typename T, typename U> inline T P0<T,U>::next(const T& in) {
  nextNoreturn(in);
  return pred.dot(buf);
}

template <typename T, typename U> const T& P0<T,U>::Pi() const {
  const static auto pi(atan2(T(1), T(1)) * T(4));
  return pi;
}

template <typename T, typename U> const U& P0<T,U>::J() const {
  const static auto i(sqrt(U(T(- 1))));
  return i;
}

template <typename T, typename U> const typename P0<T,U>::MatU& P0<T,U>::seed(const int& size, const bool& f_idft) {
  assert(0 < size);
  static vector<MatU> dft;
  static vector<MatU> idft;
  if(dft.size() <= size)
    dft.resize(size + 1, MatU());
  if(idft.size() <= size)
    idft.resize(size + 1, MatU());
  if((!f_idft) &&  dft[size].rows() == size &&  dft[size].cols() == size)
    return dft[size];
  if(  f_idft  && idft[size].rows() == size && idft[size].cols() == size)
    return idft[size];
  dft[ size].resize(size, size);
  idft[size].resize(size, size);
  auto& edft( dft[size]);
  auto& eidft(idft[size]);
  for(int i = 0; i < edft.rows(); i ++)
    for(int j = 0; j < edft.cols(); j ++) {
      const auto theta(- T(2) * Pi() * T(i) * T(j) / T(edft.rows()));
      edft(i, j)  = U(cos(  theta), sin(  theta));
      eidft(i, j) = U(cos(- theta), sin(- theta)) / U(T(size));
    }
  if(f_idft)
    return eidft;
  return edft;
}

template <typename T, typename U> const typename P0<T,U>::Mat& P0<T,U>::diff(const int& size, const bool& integrate) {
  assert(0 < size);
  static vector<Mat> D;
  static vector<Mat> I;
  if(D.size() <= size)
    D.resize(size + 1, Mat());
  if(I.size() <= size)
    I.resize(size + 1, Mat());
  if((!integrate) && D[size].rows() == size && D[size].cols() == size)
    return D[size];
  if(  integrate  && I[size].rows() == size && I[size].cols() == size)
    return I[size];
  D[size].resize(size, size);
  I[size].resize(size, size);
  auto& d(D[size]);
  auto& i(I[size]);
  auto  DD(seed(size, false));
  DD.row(0) *= U(T(0));
  auto  II(DD);
  T nd(0);
  T ni(0);
  for(int i = 1; i < DD.rows(); i ++) {
    const auto phase(- J() * T(2) * Pi() * T(i) / T(DD.rows()));
    const auto phase2(U(T(1)) / phase);
    DD.row(i) *= phase;
    II.row(i) /= phase;
    nd += abs(phase)  * abs(phase);
    ni += abs(phase2) * abs(phase2);
  }
  d = (seed(size, true) * DD).template real<T>() * sqrt(sqrt(T(DD.rows() - 1) / (nd * ni)));
  i = (seed(size, true) * II).template real<T>() * sqrt(sqrt(T(II.rows() - 1) / (nd * ni)));
  if(integrate)
    return i;
  return d;
}

template <typename T, typename U> const typename P0<T,U>::Vec& P0<T,U>::nextTaylor(const int& size) {
  assert(0 < size);
  static vector<Vec> ntayl;
  if(ntayl.size() <= size)
    ntayl.resize(size + 1, Vec());
  if(ntayl[size].size() == size)
    return ntayl[size];
  ntayl[size].resize(size);
        auto& v(ntayl[size]);
  const auto& D(diff(size, false));
        auto  dd(D.row(D.rows() - 1));
  for(int i = 0; i < v.size() - 1; i ++)
    v[i] = T(0);
  v[v.size() - 1] = T(1);
  for(int i = 2; 0 <= i; i ++) {
    const auto bv(v);
    v += dd;
    if(bv == v)
      break;
    dd = (D * dd) / T(i);
  }
  return v;
}

template <typename T, typename U> const typename P0<T,U>::Vec& P0<T,U>::nextDeepTaylor(const int& size, const int& shrink) {
  assert(0 < size);
  static vector<Vec> dtayl;
  if(dtayl.size() <= size)
    dtayl.resize(size + 1, Vec());
  if(dtayl[size].size() == size)
    return dtayl[size];
  auto& p(dtayl[size]);
  p = nextTaylor(size);
  for(int i = shrink + 1; i < p.size(); i ++) {
    const auto& q(nextTaylor(i));
    for(int j = 0; j < q.size(); j ++)
      p[p.size() + j - q.size()] += q[j];
  }
  return p /= T(p.size() - 1);
}

#define _P0_
#endif

<|endoftext|>


<commit_before><commit_msg>uncommented exit and analysis output<commit_after><|endoftext|>


<commit_before>#include "analemma.h"
#include "gtest/gtest.h"
#include <fstream>
#include <string>
#include "functions.h"
#include <vector>

TEST(AnalemmaTests, TestLancaster)
{
    stadic::Analemma suns("USA_PA_Lancaster.AP.725116_TMY3.epw");
    suns.setGeoFile("sunsGeo.rad");
    suns.setMatFile("sunsMat.rad");
    suns.setRotation(0);
    suns.setSMXFile("suns.smx");
    ASSERT_TRUE(suns.genSun());
    std::fstream iFile;
    iFile.open("sunsGeo.rad");
    ASSERT_TRUE(iFile.is_open());
    std::string line;
    int counter=0;
    while (std::getline(iFile, line)){
        counter++;
        if (counter==10){
            std::vector<std::string> vals;
            vals=stadic::trimmedSplit(line,' ');
            ASSERT_EQ(10,vals.size());
            EXPECT_EQ("solar10",vals[0]);
            EXPECT_EQ("source",vals[1]);
            EXPECT_EQ("sun10",vals[2]);
            EXPECT_EQ("0",vals[3]);
            EXPECT_EQ("0",vals[4]);
            EXPECT_EQ("4",vals[5]);
            EXPECT_EQ("0.963768",vals[6]);
            EXPECT_EQ("0.250951",vals[7]);
            EXPECT_EQ("0.0904094",vals[8]);
            EXPECT_EQ("0.533",vals[9]);
        }
    }
    iFile.close();
    EXPECT_EQ(1511, counter);
    //EXPECT_NEAR(1500,counter,15);
    iFile.open("sunsMat.rad");
    ASSERT_TRUE(iFile.is_open());
    while (std::getline(iFile, line)){
        counter++;
        if (counter==10){
            std::vector<std::string> vals;
            vals=stadic::trimmedSplit(line,' ');

        }
    }
    iFile.close();

}
<commit_msg>Added material and smx file tests to analemmatest<commit_after>#include "analemma.h"
#include "gtest/gtest.h"
#include <fstream>
#include <string>
#include "functions.h"
#include <vector>

TEST(AnalemmaTests, TestLancaster)
{
    stadic::Analemma suns("USA_PA_Lancaster.AP.725116_TMY3.epw");
    suns.setGeoFile("sunsGeo.rad");
    suns.setMatFile("sunsMat.rad");
    suns.setRotation(0);
    suns.setSMXFile("suns.smx");
    ASSERT_TRUE(suns.genSun());
    std::fstream iFile;
    iFile.open("sunsGeo.rad");
    ASSERT_TRUE(iFile.is_open());
    std::string line;
    int counter=0;
    while (std::getline(iFile, line)){
        counter++;
        if (counter==10){
            std::vector<std::string> vals;
            vals=stadic::trimmedSplit(line,' ');
            ASSERT_EQ(10,vals.size());
            EXPECT_EQ("solar10",vals[0]);
            EXPECT_EQ("source",vals[1]);
            EXPECT_EQ("sun10",vals[2]);
            EXPECT_EQ("0",vals[3]);
            EXPECT_EQ("0",vals[4]);
            EXPECT_EQ("4",vals[5]);
            EXPECT_EQ("0.963768",vals[6]);
            EXPECT_EQ("0.250951",vals[7]);
            EXPECT_EQ("0.0904094",vals[8]);
            EXPECT_EQ("0.533",vals[9]);
        }
    }
    iFile.close();
    EXPECT_EQ(1511, counter);
    //EXPECT_NEAR(1500,counter,15);
    iFile.open("sunsMat.rad");
    ASSERT_TRUE(iFile.is_open());
    counter=0;
    while (std::getline(iFile, line)){
        counter++;
        if (counter==10){
            std::vector<std::string> vals;
            vals=stadic::trimmedSplit(line,' ');
            ASSERT_EQ(9,vals.size());
            EXPECT_EQ("void",vals[0]);
            EXPECT_EQ("light",vals[1]);
            EXPECT_EQ("solar10",vals[2]);
            EXPECT_EQ("0",vals[3]);
            EXPECT_EQ("0",vals[4]);
            EXPECT_EQ("3",vals[5]);
            EXPECT_EQ("1.0",vals[6]);
            EXPECT_EQ("1.0",vals[7]);
            EXPECT_EQ("1.0",vals[8]);

        }
    }
    iFile.close();
    EXPECT_EQ(1511, counter);
    iFile.open("suns.smx");
    ASSERT_TRUE(iFile.is_open());
    counter=0;
    while (std::getline(iFile, line)){
        counter++;
        if (counter==5838){
            std::vector<std::string> vals;
            vals=stadic::trimmedSplit(line,'\t');
            ASSERT_EQ(3,vals.size());
            EXPECT_EQ("1.54e+004",vals[0]);
            EXPECT_EQ("1.54e+004",vals[1]);
            EXPECT_EQ("1.54e+004",vals[2]);

        }
    }
    iFile.close();
    EXPECT_EQ(13236360, counter);

}
<|endoftext|>


<commit_before><commit_msg>Fix Difficulty<commit_after><|endoftext|>


<commit_before><commit_msg>started extra credit<commit_after><|endoftext|>


<commit_before>#include <sstream>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/program_options.hpp>
#include <condition_variable>
#include <signal.h>
#include <chrono>
#include <atomic>
#include <thread>
#include <random>
#include <rados/librados.hpp>
#include "libzlog.h"

namespace po = boost::program_options;

//#define VERIFY_IOS
//#define SLOPPY_SEQ

struct AioState {
  zlog::Log *log;
  ceph::bufferlist append_bl;
  ceph::bufferlist read_bl;
  zlog::Log::AioCompletion *c;
  uint64_t position;
  std::chrono::time_point<std::chrono::steady_clock> submitted;
};

static std::condition_variable io_cond;
static std::mutex io_lock;
static std::atomic<uint64_t> outstanding_ios;
static std::atomic<uint64_t> ios_completed;
static std::atomic<uint64_t> ios_completed_total;
static std::atomic<uint64_t> latency_us;

static volatile int stop = 0;
static void sigint_handler(int sig) {
  stop = 1;
}

static void report()
{
  while (!stop) {
    auto start = std::chrono::steady_clock::now();
    uint64_t ios_completed_start = ios_completed;
    uint64_t latency_us_start = latency_us;

    sleep(5);
    if (stop)
      break;

    auto end = std::chrono::steady_clock::now();
    uint64_t ios_completed_end = ios_completed;
    uint64_t latency_us_end = latency_us;

    auto diff = std::chrono::duration_cast<
        std::chrono::duration<double>>(end - start);

    double ios_per_sec = (double)(ios_completed_end - ios_completed_start) / diff.count();
    double avg_us_lat = (double)(latency_us_end - latency_us_start) /
      (double)(ios_completed_end - ios_completed_start);
    double avg_ms_lat = avg_us_lat / (double)1000.0;

    std::cout << "period: " << diff.count() <<
      " total_iops: " << ios_completed_total <<
      " iops: " << ios_per_sec <<
      " avg_lat_ms: " << avg_ms_lat << std::endl;
  }
}

#ifdef VERIFY_IOS
static void verify_append_cb(zlog::Log::AioCompletion::completion_t cb,
    void *arg)
{
  AioState *s = (AioState*)arg;

  assert(s->c->get_return_value() == 0);
  s->c->release();

  assert(s->read_bl == s->append_bl);
  int sum1 = 0, sum2 = 0;
  for (unsigned i = 0; i < s->read_bl.length(); i++) {
    sum1 += (int)s->read_bl[i];
    sum2 += (int)s->append_bl[i];
  }
  assert(sum1 == sum2);

  delete s;
}
#endif

static void handle_append_cb(zlog::Log::AioCompletion::completion_t cb,
    void *arg)
{
  AioState *s = (AioState*)arg;
  auto completed = std::chrono::steady_clock::now();
  zlog::Log *log = s->log;

  ios_completed++;
  ios_completed_total++;
  outstanding_ios--;
  io_cond.notify_one();

  assert(s->c->get_return_value() == 0);
  s->c->release();
  s->c = NULL;

  auto diff_us = std::chrono::duration_cast<
    std::chrono::microseconds>(completed - s->submitted);
  latency_us += diff_us.count();

#ifdef VERIFY_IOS
  if (s->append_bl.length() > 0)
    assert(!(s->append_bl == s->read_bl));
  s->c = zlog::Log::aio_create_completion(s, verify_append_cb);
  int ret = log->AioRead(s->position, s->c, &s->read_bl);
  assert(ret == 0);
#else
  delete s;
#endif
}

static void handle_read_cb(zlog::Log::AioCompletion::completion_t cb,
    void *arg)
{
  AioState *s = (AioState*)arg;
  auto completed = std::chrono::steady_clock::now();

  ios_completed++;
  ios_completed_total++;
  outstanding_ios--;
  io_cond.notify_one();

  int op_rv = s->c->get_return_value();
  if (op_rv) {
    std::cout << op_rv << " " << s->position << std::endl;
  }
  assert(s->c->get_return_value() == 0);
  assert(s->read_bl.length() > 0);

  s->c->release();
  s->c = NULL;

  auto diff_us = std::chrono::duration_cast<
    std::chrono::microseconds>(completed - s->submitted);
  latency_us += diff_us.count();

  delete s;
}

class FakeSeqrClient : public zlog::SeqrClient {
 public:
  FakeSeqrClient(const std::string& name) :
    SeqrClient("", ""), seq_(0), name_(name)
  {
    assert(seq_.is_lock_free());
  }

  void Init(librados::IoCtx& ioctx) {
    zlog::Log log;
    int ret = zlog::Log::Open(ioctx, name_, NULL, log);
    assert(ret == 0);

    uint64_t epoch;
    ret = log.SetProjection(&epoch);
    assert(ret == 0);

    ret = log.Seal(epoch);
    assert(ret == 0);

    uint64_t position;
    ret = log.FindMaxPosition(epoch, &position);
    assert(ret == 0);

    epoch_ = epoch;
    seq_.store(position);

    std::cout << "init seq: pos=" << seq_.load() << std::endl;
  }

  void Connect() {}

  int CheckTail(uint64_t epoch, const std::string& pool,
      const std::string& name, uint64_t *position, bool next)
  {
    assert(name == name_);

    if (epoch_ < epoch)
      return -ERANGE;

    if (next) {
#ifdef SLOPPY_SEQ
      if (seq_ > 100 && seq_ % 10 == 0) {
        *position = seq_ / 2;
        seq_++;
      } else {
        uint64_t tail = seq_.fetch_add(1); // returns previous value
        *position = tail + 1;
      }
#else
      uint64_t tail = seq_.fetch_add(1); // returns previous value
      *position = tail + 1;
#endif
    } else
      *position = seq_.load();

    return 0;
  }

 private:
  std::atomic<uint64_t> seq_;
  std::string name_;
  uint64_t epoch_;
};

int main(int argc, char **argv)
{
  int width;
  std::string pool;
  std::string logname_req;
  int qdepth;
  int iosize;
  bool read_mode;
  bool sync;

  po::options_description desc("Allowed options");
  desc.add_options()
    ("pool", po::value<std::string>(&pool)->required(), "Pool name")
    ("logname", po::value<std::string>(&logname_req)->default_value(""), "Log name")
    ("width", po::value<int>(&width)->required(), "Width")
    ("qdepth", po::value<int>(&qdepth)->default_value(1), "Queue depth")
    ("iosize", po::value<int>(&iosize)->default_value(0), "IO Size")
    ("read", po::value<bool>(&read_mode)->default_value(false), "Read mode")
    ("sync", po::value<bool>(&sync)->default_value(false), "Sync mode")
  ;

  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, desc), vm);
  po::notify(vm);

  assert(qdepth > 0);

  if (read_mode)
    assert(logname_req.length());

  // connect to rados
  librados::Rados cluster;
  cluster.init(NULL);
  cluster.conf_read_file(NULL);
  cluster.conf_parse_env(NULL);
  int ret = cluster.connect();
  assert(ret == 0);

  // open pool i/o context
  librados::IoCtx ioctx;
  ret = cluster.ioctx_create(pool.c_str(), ioctx);
  assert(ret == 0);

  signal(SIGINT, sigint_handler);

  // build a log name
  std::string logname;
  {
    std::stringstream ss;
    if (logname_req.size())
      ss << logname_req;
    else {
      boost::uuids::uuid uuid = boost::uuids::random_generator()();
      ss << uuid;
    }
    ss << ".log";
    logname = ss.str();
  }

  // setup log instance
  zlog::Log log;
  FakeSeqrClient client(logname);
  ret = zlog::Log::OpenOrCreate(ioctx, logname, width, &client, log);
  client.Init(ioctx);
  assert(ret == 0);

  // this is just a little hack that forces the epoch to be refreshed in the
  // log instance. otherwise when we blast out a bunch of async requests they
  // all end up having old epochs.
  ceph::bufferlist bl;
  log.Append(bl);

  /*
   * For read mode we look up the current tail and then issue random reads
   * within the range of the log.
   */
  uint64_t tail = 0;
  if (read_mode) {
    ret = log.CheckTail(&tail, false);
    assert(!ret);
    // teardown/setup is sloppy, so we trim off a bit so we don't hit any
    // unwritten entries.
    std::cout << "read mode tail: " << tail << std::endl;
    tail -= 50;
    assert(tail > 0);
  }
  std::random_device rd;
  std::mt19937 gen(rd());
  // skip the front of the log if there was some problems getting this setup
  // and those entries were skipped or filled.
  std::uniform_int_distribution<unsigned long long> dis(50, tail);
  if (read_mode)
    std::cout << "tail generation: " << dis.min() << " .. " << dis.max() << std::endl;

  std::unique_lock<std::mutex> lock(io_lock);

  ios_completed = 0;
  ios_completed_total = 0;
  outstanding_ios = 0;
  latency_us = 0;

  char iobuf[iosize];

  std::thread reporting_thread(report);

  if (sync) {
    std::cout << "sync mode: ignoring qdepth (qdepth=1)" << std::endl;

    while (!stop) {
      auto start = std::chrono::steady_clock::now();
      if (read_mode) {
        uint64_t pos = dis(gen);
        ceph::bufferlist bl;
        int ret = log.Read(pos, bl);
        assert(ret == 0);
        assert(bl.length() > 0);
      } else {
        for (unsigned i = 0; i < iosize; i++) {
          iobuf[i] = (char)rand();
        }
        uint64_t pos = 0;
        ceph::bufferlist bl;
        bl.append(iobuf, iosize);
        int ret = log.Append(bl, &pos);
        assert(ret == 0);
        assert(pos > 0);
      }
      auto end = std::chrono::steady_clock::now();
      auto diff_us = std::chrono::duration_cast<
        std::chrono::microseconds>(end - start);
      latency_us += diff_us.count();
      //std::cout << diff_us.count() << std::endl;
      ios_completed++;
      ios_completed_total++;
    }

  } else {
    for (;;) {
      while (outstanding_ios < qdepth) {
        AioState *state = new AioState;
        state->log = &log;
        if (read_mode) {
          state->position = dis(gen);
          state->c = zlog::Log::aio_create_completion(state, handle_read_cb);
          assert(state->c);
          state->submitted = std::chrono::steady_clock::now();
          ret = log.AioRead(state->position, state->c, &state->read_bl);
          assert(ret == 0);
        } else {
          state->c = zlog::Log::aio_create_completion(state, handle_append_cb);
          assert(state->c);
          for (unsigned i = 0; i < iosize; i++) {
            iobuf[i] = (char)rand();
          }
          state->append_bl.append(iobuf, iosize);
          state->submitted = std::chrono::steady_clock::now();
          ret = log.AioAppend(state->c, state->append_bl, &state->position);
          assert(ret == 0);
        }

        outstanding_ios++;
      }
      io_cond.wait(lock, [&]{ return outstanding_ios < qdepth; });

      if (stop)
        break;
    }

    // draining the ios is useful for running the read workload because
    // otherwise there is a section near the tail of the log that has unwritten
    // entries that would otherwise need to be handled in some way when they
    // were read.
    while (1) {
      std::cout << "draining ios: " << outstanding_ios << " remaining" << std::endl;
      if (outstanding_ios == 0)
        break;
      sleep(1);
    }
  }

  reporting_thread.join();

  return 0;
}
<commit_msg>bench: support real sequencer server<commit_after>#include <sstream>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/program_options.hpp>
#include <condition_variable>
#include <signal.h>
#include <chrono>
#include <atomic>
#include <thread>
#include <random>
#include <rados/librados.hpp>
#include "libzlog.h"

namespace po = boost::program_options;

//#define VERIFY_IOS
//#define SLOPPY_SEQ

struct AioState {
  zlog::Log *log;
  ceph::bufferlist append_bl;
  ceph::bufferlist read_bl;
  zlog::Log::AioCompletion *c;
  uint64_t position;
  std::chrono::time_point<std::chrono::steady_clock> submitted;
};

static std::condition_variable io_cond;
static std::mutex io_lock;
static std::atomic<uint64_t> outstanding_ios;
static std::atomic<uint64_t> ios_completed;
static std::atomic<uint64_t> ios_completed_total;
static std::atomic<uint64_t> latency_us;

static volatile int stop = 0;
static void sigint_handler(int sig) {
  stop = 1;
}

static void report()
{
  while (!stop) {
    auto start = std::chrono::steady_clock::now();
    uint64_t ios_completed_start = ios_completed;
    uint64_t latency_us_start = latency_us;

    sleep(5);
    if (stop)
      break;

    auto end = std::chrono::steady_clock::now();
    uint64_t ios_completed_end = ios_completed;
    uint64_t latency_us_end = latency_us;

    auto diff = std::chrono::duration_cast<
        std::chrono::duration<double>>(end - start);

    double ios_per_sec = (double)(ios_completed_end - ios_completed_start) / diff.count();
    double avg_us_lat = (double)(latency_us_end - latency_us_start) /
      (double)(ios_completed_end - ios_completed_start);
    double avg_ms_lat = avg_us_lat / (double)1000.0;

    std::cout << "period: " << diff.count() <<
      " total_iops: " << ios_completed_total <<
      " iops: " << ios_per_sec <<
      " avg_lat_ms: " << avg_ms_lat << std::endl;
  }
}

#ifdef VERIFY_IOS
static void verify_append_cb(zlog::Log::AioCompletion::completion_t cb,
    void *arg)
{
  AioState *s = (AioState*)arg;

  assert(s->c->get_return_value() == 0);
  s->c->release();

  assert(s->read_bl == s->append_bl);
  int sum1 = 0, sum2 = 0;
  for (unsigned i = 0; i < s->read_bl.length(); i++) {
    sum1 += (int)s->read_bl[i];
    sum2 += (int)s->append_bl[i];
  }
  assert(sum1 == sum2);

  delete s;
}
#endif

static void handle_append_cb(zlog::Log::AioCompletion::completion_t cb,
    void *arg)
{
  AioState *s = (AioState*)arg;
  auto completed = std::chrono::steady_clock::now();
  zlog::Log *log = s->log;

  ios_completed++;
  ios_completed_total++;
  outstanding_ios--;
  io_cond.notify_one();

  assert(s->c->get_return_value() == 0);
  s->c->release();
  s->c = NULL;

  auto diff_us = std::chrono::duration_cast<
    std::chrono::microseconds>(completed - s->submitted);
  latency_us += diff_us.count();

#ifdef VERIFY_IOS
  if (s->append_bl.length() > 0)
    assert(!(s->append_bl == s->read_bl));
  s->c = zlog::Log::aio_create_completion(s, verify_append_cb);
  int ret = log->AioRead(s->position, s->c, &s->read_bl);
  assert(ret == 0);
#else
  delete s;
#endif
}

static void handle_read_cb(zlog::Log::AioCompletion::completion_t cb,
    void *arg)
{
  AioState *s = (AioState*)arg;
  auto completed = std::chrono::steady_clock::now();

  ios_completed++;
  ios_completed_total++;
  outstanding_ios--;
  io_cond.notify_one();

  int op_rv = s->c->get_return_value();
  if (op_rv) {
    std::cout << op_rv << " " << s->position << std::endl;
  }
  assert(s->c->get_return_value() == 0);
  assert(s->read_bl.length() > 0);

  s->c->release();
  s->c = NULL;

  auto diff_us = std::chrono::duration_cast<
    std::chrono::microseconds>(completed - s->submitted);
  latency_us += diff_us.count();

  delete s;
}

class FakeSeqrClient : public zlog::SeqrClient {
 public:
  FakeSeqrClient(const std::string& name) :
    SeqrClient("", ""), seq_(0), name_(name)
  {
    assert(seq_.is_lock_free());
  }

  void Init(librados::IoCtx& ioctx) {
    zlog::Log log;
    int ret = zlog::Log::Open(ioctx, name_, NULL, log);
    assert(ret == 0);

    uint64_t epoch;
    ret = log.SetProjection(&epoch);
    assert(ret == 0);

    ret = log.Seal(epoch);
    assert(ret == 0);

    uint64_t position;
    ret = log.FindMaxPosition(epoch, &position);
    assert(ret == 0);

    epoch_ = epoch;
    seq_.store(position);

    std::cout << "init seq: pos=" << seq_.load() << std::endl;
  }

  void Connect() {}

  int CheckTail(uint64_t epoch, const std::string& pool,
      const std::string& name, uint64_t *position, bool next)
  {
    assert(name == name_);

    if (epoch_ < epoch)
      return -ERANGE;

    if (next) {
#ifdef SLOPPY_SEQ
      if (seq_ > 100 && seq_ % 10 == 0) {
        *position = seq_ / 2;
        seq_++;
      } else {
        uint64_t tail = seq_.fetch_add(1); // returns previous value
        *position = tail + 1;
      }
#else
      uint64_t tail = seq_.fetch_add(1); // returns previous value
      *position = tail + 1;
#endif
    } else
      *position = seq_.load();

    return 0;
  }

 private:
  std::atomic<uint64_t> seq_;
  std::string name_;
  uint64_t epoch_;
};

int main(int argc, char **argv)
{
  int width;
  std::string pool;
  std::string logname_req;
  int qdepth;
  int iosize;
  bool read_mode;
  bool sync;
  std::string server;
  std::string port;

  po::options_description desc("Allowed options");
  desc.add_options()
    ("pool", po::value<std::string>(&pool)->required(), "Pool name")
    ("logname", po::value<std::string>(&logname_req)->default_value(""), "Log name")
    ("width", po::value<int>(&width)->required(), "Width")
    ("qdepth", po::value<int>(&qdepth)->default_value(1), "Queue depth")
    ("iosize", po::value<int>(&iosize)->default_value(0), "IO Size")
    ("read", po::value<bool>(&read_mode)->default_value(false), "Read mode")
    ("sync", po::value<bool>(&sync)->default_value(false), "Sync mode")
    ("server", po::value<std::string>(&server)->required(), "Server host")
    ("port", po::value<std::string>(&port)->required(), "Server port")
  ;

  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, desc), vm);
  po::notify(vm);

  assert(qdepth > 0);

  if (read_mode)
    assert(logname_req.length());

  // connect to rados
  librados::Rados cluster;
  cluster.init(NULL);
  cluster.conf_read_file(NULL);
  cluster.conf_parse_env(NULL);
  int ret = cluster.connect();
  assert(ret == 0);

  // open pool i/o context
  librados::IoCtx ioctx;
  ret = cluster.ioctx_create(pool.c_str(), ioctx);
  assert(ret == 0);

  signal(SIGINT, sigint_handler);

  // build a log name
  std::string logname;
  {
    std::stringstream ss;
    if (logname_req.size())
      ss << logname_req;
    else {
      boost::uuids::uuid uuid = boost::uuids::random_generator()();
      ss << uuid;
    }
    ss << ".log";
    logname = ss.str();
  }

  // setup log instance
  zlog::SeqrClient *client;
  if (server.length()) {
    client = new zlog::SeqrClient(server.c_str(), port.c_str());
    client->Connect();
  } else {
    client = new FakeSeqrClient(logname);
  }
  zlog::Log log;
  ret = zlog::Log::OpenOrCreate(ioctx, logname, width, client, log);
  assert(ret == 0);

  if (server.length() == 0) {
    FakeSeqrClient *c = static_cast<FakeSeqrClient*>(client);
    c->Init(ioctx);
  }

  // this is just a little hack that forces the epoch to be refreshed in the
  // log instance. otherwise when we blast out a bunch of async requests they
  // all end up having old epochs.
  ceph::bufferlist bl;
  log.Append(bl);

  /*
   * For read mode we look up the current tail and then issue random reads
   * within the range of the log.
   */
  uint64_t tail = 0;
  if (read_mode) {
    ret = log.CheckTail(&tail, false);
    assert(!ret);
    // teardown/setup is sloppy, so we trim off a bit so we don't hit any
    // unwritten entries.
    std::cout << "read mode tail: " << tail << std::endl;
    tail -= 50;
    assert(tail > 0);
  }
  std::random_device rd;
  std::mt19937 gen(rd());
  // skip the front of the log if there was some problems getting this setup
  // and those entries were skipped or filled.
  std::uniform_int_distribution<unsigned long long> dis(50, tail);
  if (read_mode)
    std::cout << "tail generation: " << dis.min() << " .. " << dis.max() << std::endl;

  std::unique_lock<std::mutex> lock(io_lock);

  ios_completed = 0;
  ios_completed_total = 0;
  outstanding_ios = 0;
  latency_us = 0;

  char iobuf[iosize];

  std::thread reporting_thread(report);

  if (sync) {
    std::cout << "sync mode: ignoring qdepth (qdepth=1)" << std::endl;

    while (!stop) {
      auto start = std::chrono::steady_clock::now();
      if (read_mode) {
        uint64_t pos = dis(gen);
        ceph::bufferlist bl;
        int ret = log.Read(pos, bl);
        assert(ret == 0);
        assert(bl.length() > 0);
      } else {
        for (unsigned i = 0; i < iosize; i++) {
          iobuf[i] = (char)rand();
        }
        uint64_t pos = 0;
        ceph::bufferlist bl;
        bl.append(iobuf, iosize);
        int ret = log.Append(bl, &pos);
        assert(ret == 0);
        assert(pos > 0);
      }
      auto end = std::chrono::steady_clock::now();
      auto diff_us = std::chrono::duration_cast<
        std::chrono::microseconds>(end - start);
      latency_us += diff_us.count();
      //std::cout << diff_us.count() << std::endl;
      ios_completed++;
      ios_completed_total++;
    }

  } else {
    for (;;) {
      while (outstanding_ios < qdepth) {
        AioState *state = new AioState;
        state->log = &log;
        if (read_mode) {
          state->position = dis(gen);
          state->c = zlog::Log::aio_create_completion(state, handle_read_cb);
          assert(state->c);
          state->submitted = std::chrono::steady_clock::now();
          ret = log.AioRead(state->position, state->c, &state->read_bl);
          assert(ret == 0);
        } else {
          state->c = zlog::Log::aio_create_completion(state, handle_append_cb);
          assert(state->c);
          for (unsigned i = 0; i < iosize; i++) {
            iobuf[i] = (char)rand();
          }
          state->append_bl.append(iobuf, iosize);
          state->submitted = std::chrono::steady_clock::now();
          ret = log.AioAppend(state->c, state->append_bl, &state->position);
          assert(ret == 0);
        }

        outstanding_ios++;
      }
      io_cond.wait(lock, [&]{ return outstanding_ios < qdepth; });

      if (stop)
        break;
    }

    // draining the ios is useful for running the read workload because
    // otherwise there is a section near the tail of the log that has unwritten
    // entries that would otherwise need to be handled in some way when they
    // were read.
    while (1) {
      std::cout << "draining ios: " << outstanding_ios << " remaining" << std::endl;
      if (outstanding_ios == 0)
        break;
      sleep(1);
    }
  }

  reporting_thread.join();

  return 0;
}
<|endoftext|>


<commit_before><commit_msg>Allow cross-compilation to Android.<commit_after><|endoftext|>