/*++ Copyright (c) Microsoft Corporation Licensed under the MIT license. Module Name: - telemetry.hpp Abstract: - This module is used for recording all telemetry feedback from the console Author(s): - Evan Wirt (EvanWi) 09-Jul-2014 - Kourosh Mehrain (KMehrain) 09-Jul-2014 - Stephen Somuah (StSomuah) 09-Jul-2014 - Anup Manandhar (AnupM) 09-Jul-2014 --*/ #pragma once #include class Telemetry { public: // Implement this as a singleton class. static Telemetry& Instance() { static Telemetry s_Instance; return s_Instance; } void SetUserInteractive(); void SetWindowSizeChanged(); void SetContextMenuUsed(); void SetKeyboardTextSelectionUsed(); void SetKeyboardTextEditingUsed(); void SetCtrlPgUpPgDnUsed(); void LogCtrlShiftCProcUsed(); void LogCtrlShiftCRawUsed(); void LogCtrlShiftVProcUsed(); void LogCtrlShiftVRawUsed(); void LogQuickEditCopyProcUsed(); void LogQuickEditCopyRawUsed(); void LogQuickEditPasteProcUsed(); void LogQuickEditPasteRawUsed(); void LogColorSelectionUsed(); void LogFindDialogNextClicked(const unsigned int iStringLength, const bool fDirectionDown, const bool fMatchCase); void LogProcessConnected(const HANDLE hProcess); void FindDialogClosed(); void WriteFinalTraceLog(); void LogRipMessage(_In_z_ const char* pszMessage, ...) const; bool IsUserInteractive(); // Names are from the external API call names. Note that some names can be different // than the internal API calls. // Don't worry about the following APIs, because they are external to our conhost codebase and hard to track through // telemetry: GetStdHandle, SetConsoleCtrlHandler, SetStdHandle // We can't differentiate between these apis, so just log the "-Ex" versions: GetConsoleScreenBufferInfo / GetConsoleScreenBufferInfoEx, // GetCurrentConsoleFontEx / GetCurrentConsoleFont enum ApiCall { AddConsoleAlias = 0, AllocConsole, AttachConsole, CreateConsoleScreenBuffer, FillConsoleOutputAttribute, FillConsoleOutputCharacter, FlushConsoleInputBuffer, FreeConsole, GenerateConsoleCtrlEvent, GetConsoleAlias, GetConsoleAliases, GetConsoleAliasesLength, GetConsoleAliasExes, GetConsoleAliasExesLength, GetConsoleCP, GetConsoleCursorInfo, GetConsoleDisplayMode, GetConsoleFontSize, GetConsoleHistoryInfo, GetConsoleMode, GetConsoleLangId, GetConsoleOriginalTitle, GetConsoleOutputCP, GetConsoleProcessList, GetConsoleScreenBufferInfoEx, GetConsoleSelectionInfo, GetConsoleTitle, GetConsoleWindow, GetCurrentConsoleFontEx, GetLargestConsoleWindowSize, GetNumberOfConsoleInputEvents, GetNumberOfConsoleMouseButtons, PeekConsoleInput, ReadConsole, ReadConsoleInput, ReadConsoleOutput, ReadConsoleOutputAttribute, ReadConsoleOutputCharacter, ScrollConsoleScreenBuffer, SetConsoleActiveScreenBuffer, SetConsoleCP, SetConsoleCursorInfo, SetConsoleCursorPosition, SetConsoleDisplayMode, SetConsoleHistoryInfo, SetConsoleMode, SetConsoleOutputCP, SetConsoleScreenBufferInfoEx, SetConsoleScreenBufferSize, SetConsoleTextAttribute, SetConsoleTitle, SetConsoleWindowInfo, SetCurrentConsoleFontEx, WriteConsole, WriteConsoleInput, WriteConsoleOutput, WriteConsoleOutputAttribute, WriteConsoleOutputCharacter, // Only use this last enum as a count of the number of api enums. NUMBER_OF_APIS }; void LogApiCall(const ApiCall api); void LogApiCall(const ApiCall api, const BOOLEAN fUnicode); private: // Used to prevent multiple instances Telemetry(); ~Telemetry(); Telemetry(Telemetry const&); void operator=(Telemetry const&); bool FindProcessName(const WCHAR* pszProcessName, _Out_ size_t* iPosition) const; void TotalCodesForPreviousProcess(); static const int c_iMaxProcessesConnected = 100; TraceLoggingActivity _activity; float _fpFindStringLengthAverage; float _fpDirectionDownAverage; float _fpMatchCaseAverage; unsigned int _uiFindNextClickedTotal; unsigned int _uiColorSelectionUsed; time_t _tStartedAt; WCHAR const* const c_pwszBashExeName = L"bash.exe"; // The current recommendation is to keep telemetry events 4KB or less, so let's keep our array at less than 2KB (1000 * 2 bytes). WCHAR _wchProcessFileNames[1000]; // Index into our specially packed string, where to insert the next string. size_t _iProcessFileNamesNext; // Index for the currently connected process. size_t _iProcessConnectedCurrently; // An array of indexes into the _wchProcessFileNames array, which point to the individual process names. size_t _rgiProcessFileNameIndex[c_iMaxProcessesConnected]; // Number of times each process has connected to the console. unsigned int _rguiProcessFileNamesCount[c_iMaxProcessesConnected]; // To speed up searching the Process Names, create an alphabetically sorted index. size_t _rgiAlphabeticalIndex[c_iMaxProcessesConnected]; // Total of how many codes each process used unsigned int _rguiProcessFileNamesCodesCount[c_iMaxProcessesConnected]; // Total of how many failed codes each process used unsigned int _rguiProcessFileNamesFailedCodesCount[c_iMaxProcessesConnected]; // Total of how many failed codes each process used outside the valid range. unsigned int _rguiProcessFileNamesFailedOutsideCodesCount[c_iMaxProcessesConnected]; unsigned int _rguiTimesApiUsed[NUMBER_OF_APIS]; // Most of this array will be empty, and is only used if an API has an ansi specific variant. unsigned int _rguiTimesApiUsedAnsi[NUMBER_OF_APIS]; // Total number of file names we've added. UINT16 _uiNumberProcessFileNames; bool _fBashUsed; bool _fKeyboardTextEditingUsed; bool _fKeyboardTextSelectionUsed; bool _fUserInteractiveForTelemetry; bool _fCtrlPgUpPgDnUsed; // Linux copy and paste keyboard shortcut telemetry unsigned int _uiCtrlShiftCProcUsed; unsigned int _uiCtrlShiftCRawUsed; unsigned int _uiCtrlShiftVProcUsed; unsigned int _uiCtrlShiftVRawUsed; // Quick edit copy and paste usage telemetry unsigned int _uiQuickEditCopyProcUsed; unsigned int _uiQuickEditCopyRawUsed; unsigned int _uiQuickEditPasteProcUsed; unsigned int _uiQuickEditPasteRawUsed; }; // Log the RIPMSG through telemetry, and also through a normal OutputDebugStringW call. // These are drop-in substitutes for the RIPMSG0-4 macros from /windows/Core/ntcon2/conhost/consrv.h #define RIPMSG0(flags, msg) Telemetry::Instance().LogRipMessage(msg); #define RIPMSG1(flags, msg, a) Telemetry::Instance().LogRipMessage(msg, a); #define RIPMSG2(flags, msg, a, b) Telemetry::Instance().LogRipMessage(msg, a, b); #define RIPMSG3(flags, msg, a, b, c) Telemetry::Instance().LogRipMessage(msg, a, b, c); #define RIPMSG4(flags, msg, a, b, c, d) Telemetry::Instance().LogRipMessage(msg, a, b, c, d);