diff options
| -rw-r--r-- | binaryninjaapi.h | 35 | ||||
| -rw-r--r-- | binaryninjacore.h | 33 | ||||
| -rw-r--r-- | binaryview.cpp | 6 | ||||
| -rw-r--r-- | docs/about/open-source.md | 1 | ||||
| -rw-r--r-- | filemetadata.cpp | 7 | ||||
| -rw-r--r-- | log.cpp | 156 | ||||
| -rw-r--r-- | python/binaryview.py | 7 | ||||
| -rw-r--r-- | python/filemetadata.py | 4 | ||||
| -rw-r--r-- | python/function.py | 5 | ||||
| -rw-r--r-- | python/log.py | 58 | ||||
| -rw-r--r-- | python/types.py | 12 | ||||
| -rw-r--r-- | rust/src/lib.rs | 8 | ||||
| -rw-r--r-- | suite/api_test.py | 2 | ||||
| -rw-r--r-- | ui/filecontext.h | 1 | ||||
| -rw-r--r-- | ui/logview.h | 243 | ||||
| -rw-r--r-- | ui/uicontext.h | 2 | ||||
| -rw-r--r-- | ui/viewframe.h | 10 |
17 files changed, 472 insertions, 118 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 351c9797..c9c0ea70 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -38,6 +38,7 @@ #include <type_traits> #include <variant> #include <optional> +#include <memory> #include "binaryninjacore.h" #include "json/json.h" @@ -436,7 +437,7 @@ namespace BinaryNinja { class LogListener { - static void LogMessageCallback(void* ctxt, BNLogLevel level, const char* msg); + static void LogMessageCallback(void* ctxt, size_t session, BNLogLevel level, const char* msg, const char* logger_name = "", size_t tid = 0); static void CloseLogCallback(void* ctxt); static BNLogLevel GetLogLevelCallback(void* ctxt); @@ -447,7 +448,7 @@ namespace BinaryNinja { static void UnregisterLogListener(LogListener* listener); static void UpdateLogListeners(); - virtual void LogMessage(BNLogLevel level, const std::string& msg) = 0; + virtual void LogMessage(size_t session, BNLogLevel level, const std::string& msg, const std::string& logger_name = "", size_t tid = 0) = 0; virtual void CloseLog() {} virtual BNLogLevel GetLogLevel() { return WarningLog; } }; @@ -538,6 +539,33 @@ namespace BinaryNinja { bool LogToFile(BNLogLevel minimumLevel, const std::string& path, bool append = false); void CloseLogs(); + class FileMetadata; + class BinaryView; + class Logger: public CoreRefCountObject<BNLogger, BNNewLoggerReference, BNFreeLogger> + { + size_t GetThreadId() const; + public: + Logger(BNLogger* logger); + Logger(const std::string& loggerName, size_t sessionId = 0); + void Log(BNLogLevel level, const char* fmt, ...); + void LogDebug(const char* fmt, ...); + void LogInfo(const char* fmt, ...); + void LogWarn(const char* fmt, ...); + void LogError(const char* fmt, ...); + void LogAlert(const char* fmt, ...); + std::string GetName(); + size_t GetSessionId(); + }; + + class LogRegistry + { + public: + static Ref<Logger> CreateLogger(const std::string& loggerName, size_t sessionId = 0); + static Ref<Logger> GetLogger(const std::string& loggerName, size_t sessionId = 0); + static std::vector<std::string> GetLoggerNames(); + static void RegisterLoggerCallback(const std::function<void(const std::string&)>& cb); + }; + std::string EscapeString(const std::string& s); std::string UnescapeString(const std::string& s); @@ -1007,6 +1035,7 @@ namespace BinaryNinja { BinaryNinja::Ref<BinaryNinja::BinaryView> GetViewOfType(const std::string& name); std::vector<std::string> GetExistingViews() const; + size_t GetSessionId() const; }; class Function; @@ -2141,6 +2170,8 @@ namespace BinaryNinja { Ref<Structure> CreateStructureFromOffsetAccess(const QualifiedName& type, bool* newMemberAdded) const; Confidence<Ref<Type>> CreateStructureMemberFromAccess(const QualifiedName& name, uint64_t offset) const; + + Ref<Logger> CreateLogger(const std::string& name); }; diff --git a/binaryninjacore.h b/binaryninjacore.h index c86b501e..53a125c4 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -21,9 +21,15 @@ #ifndef __BINARYNINJACORE_H__ #define __BINARYNINJACORE_H__ +#ifdef __cplusplus #include <cstdint> #include <cstddef> #include <cstdlib> +#else +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#endif // Current ABI version for linking to the core. This is incremented any time // there are changes to the API that affect linking, including new functions, @@ -222,6 +228,7 @@ extern "C" struct BNDebugInfo; struct BNDebugInfoParser; struct BNSecretsProvider; + struct BNLogger; //! Console log levels @@ -1291,7 +1298,7 @@ extern "C" struct BNLogListener { void* context; - void (*log)(void* ctxt, BNLogLevel level, const char* msg); + void (*log)(void* ctxt, size_t sessionId, BNLogLevel level, const char* msg, const char* logger_name, size_t tid); void (*close)(void* ctxt); BNLogLevel (*getLogLevel)(void* ctxt); }; @@ -2803,10 +2810,10 @@ extern "C" // Logging #ifdef __GNUC__ - __attribute__((format(printf, 2, 3))) + __attribute__((format(printf, 5, 6))) #endif BINARYNINJACOREAPI void - BNLog(BNLogLevel level, const char* fmt, ...); + BNLog(size_t session, BNLogLevel level, const char* logger_name, size_t tid, const char* fmt, ...); #ifdef __GNUC__ __attribute__((format(printf, 1, 2))) @@ -2838,7 +2845,24 @@ extern "C" BINARYNINJACOREAPI void BNLogAlert(const char* fmt, ...); - BINARYNINJACOREAPI void BNLogString(BNLogLevel level, const char* str); + BINARYNINJACOREAPI void BNLogString(size_t session, BNLogLevel level, const char* logger_name, size_t tid, const char* str); + + + BINARYNINJACOREAPI BNLogger* BNNewLoggerReference(BNLogger* logger); + BINARYNINJACOREAPI void BNFreeLogger(BNLogger* logger); + +#ifdef __GNUC__ + __attribute__((format(printf, 3, 4))) +#endif + BINARYNINJACOREAPI void BNLoggerLog(BNLogger* logger, BNLogLevel level, const char* fmt, ...); + BINARYNINJACOREAPI void BNLoggerLogString(BNLogger* logger, BNLogLevel level, const char* msg); + + BINARYNINJACOREAPI char* BNLoggerGetName(BNLogger* logger); + BINARYNINJACOREAPI size_t BNLoggerGetSessionId(BNLogger* logger); + BINARYNINJACOREAPI BNLogger* BNLogCreateLogger(const char* loggerName, size_t sessionId); + BINARYNINJACOREAPI BNLogger* BNLogGetLogger(const char* loggerName, size_t sessionId); + BINARYNINJACOREAPI char** BNLogGetLoggerNames(size_t* count); + BINARYNINJACOREAPI void BNLogRegisterLoggerCallback(void (*cb)(const char* name, void* ctxt), void* ctxt); BINARYNINJACOREAPI void BNRegisterLogListener(BNLogListener* listener); BINARYNINJACOREAPI void BNUnregisterLogListener(BNLogListener* listener); @@ -3031,6 +3055,7 @@ extern "C" BINARYNINJACOREAPI BNBinaryView* BNGetFileViewOfType(BNFileMetadata* file, const char* name); BINARYNINJACOREAPI char** BNGetExistingViews(BNFileMetadata* file, size_t* count); + BINARYNINJACOREAPI size_t BNFileMetadataGetSessionId(BNFileMetadata* file); BINARYNINJACOREAPI bool BNIsSnapshotDataAppliedWithoutError(BNFileMetadata* view); diff --git a/binaryview.cpp b/binaryview.cpp index ba812919..541c6ca9 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -3769,6 +3769,12 @@ Confidence<Ref<Type>> BinaryView::CreateStructureMemberFromAccess(const Qualifie } +Ref<Logger> BinaryView::CreateLogger(const string& name) +{ + return LogRegistry::CreateLogger(name, GetFile()->GetSessionId()); +} + + Relocation::Relocation(BNRelocation* reloc) { m_object = reloc; diff --git a/docs/about/open-source.md b/docs/about/open-source.md index 4cb8db19..3fff975b 100644 --- a/docs/about/open-source.md +++ b/docs/about/open-source.md @@ -40,6 +40,7 @@ The previous tools are used in the generation of our documentation, but are not - [curl] ([curl license] - MIT) - [nom] ([nom license] - MIT) - [xxHash] ([xxHash License] - 2-clause BSD) + - [spdlog] ([spdlog license] - MIT) * Other - [yasm] ([yasm license] - 2-clause BSD) used for assembling x86 and x64 diff --git a/filemetadata.cpp b/filemetadata.cpp index e240cd1b..57ebcc2d 100644 --- a/filemetadata.cpp +++ b/filemetadata.cpp @@ -421,6 +421,13 @@ std::vector<std::string> FileMetadata::GetExistingViews() const return result; } + +size_t FileMetadata::GetSessionId() const +{ + return BNFileMetadataGetSessionId(m_object); +} + + bool FileMetadata::IsSnapshotDataAppliedWithoutError() const { return BNIsSnapshotDataAppliedWithoutError(m_object); @@ -21,16 +21,16 @@ #define _CRT_SECURE_NO_WARNINGS #include <stdarg.h> #include <stdio.h> +#include <thread> #include "binaryninjaapi.h" using namespace BinaryNinja; using namespace std; - -void LogListener::LogMessageCallback(void* ctxt, BNLogLevel level, const char* msg) +void LogListener::LogMessageCallback(void* ctxt, size_t session, BNLogLevel level, const char* msg, const char* logger_name, size_t tid) { LogListener* listener = (LogListener*)ctxt; - listener->LogMessage(level, msg); + listener->LogMessage(session, level, msg, logger_name, tid); } @@ -75,7 +75,7 @@ void LogListener::UpdateLogListeners() } -static void PerformLog(BNLogLevel level, const char* fmt, va_list args) +static void PerformLog(size_t session, BNLogLevel level, const string& logger_name, size_t tid, const char* fmt, va_list args) { #if defined(_MSC_VER) int len = _vscprintf(fmt, args); @@ -85,13 +85,13 @@ static void PerformLog(BNLogLevel level, const char* fmt, va_list args) if (!msg) return; if (vsnprintf(msg, len + 1, fmt, args) >= 0) - BNLog(level, "%s", msg); + BNLog(session, level, logger_name.c_str(), tid, "%s", msg); free(msg); #else char* msg; if (vasprintf(&msg, fmt, args) < 0) return; - BNLog(level, "%s", msg); + BNLog(session, level, logger_name.c_str(), tid, "%s", msg); free(msg); #endif } @@ -101,7 +101,7 @@ void BinaryNinja::Log(BNLogLevel level, const char* fmt, ...) { va_list args; va_start(args, fmt); - PerformLog(level, fmt, args); + PerformLog(0, level, "", 0, fmt, args); va_end(args); } @@ -110,7 +110,7 @@ void BinaryNinja::LogDebug(const char* fmt, ...) { va_list args; va_start(args, fmt); - PerformLog(DebugLog, fmt, args); + PerformLog(0, DebugLog, "", 0, fmt, args); va_end(args); } @@ -119,7 +119,7 @@ void BinaryNinja::LogInfo(const char* fmt, ...) { va_list args; va_start(args, fmt); - PerformLog(InfoLog, fmt, args); + PerformLog(0, InfoLog, "", 0, fmt, args); va_end(args); } @@ -128,7 +128,7 @@ void BinaryNinja::LogWarn(const char* fmt, ...) { va_list args; va_start(args, fmt); - PerformLog(WarningLog, fmt, args); + PerformLog(0, WarningLog, "", 0, fmt, args); va_end(args); } @@ -137,7 +137,7 @@ void BinaryNinja::LogError(const char* fmt, ...) { va_list args; va_start(args, fmt); - PerformLog(ErrorLog, fmt, args); + PerformLog(0, ErrorLog, "", 0, fmt, args); va_end(args); } @@ -146,7 +146,7 @@ void BinaryNinja::LogAlert(const char* fmt, ...) { va_list args; va_start(args, fmt); - PerformLog(AlertLog, fmt, args); + PerformLog(0, AlertLog, "", 0, fmt, args); va_end(args); } @@ -173,3 +173,135 @@ void BinaryNinja::CloseLogs() { BNCloseLogs(); } + +size_t Logger::GetThreadId() const +{ + return std::hash<std::thread::id>{}(std::this_thread::get_id()); +} + +Logger::Logger(BNLogger* logger) +{ + m_object = logger; +} + + +Logger::Logger(const string& loggerName, size_t sessionId) +{ + m_object = BNLogCreateLogger(loggerName.c_str(), sessionId); +} + + +void Logger::Log(BNLogLevel level, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + PerformLog(GetSessionId(), level, GetName(), GetThreadId(), fmt, args); + va_end(args); +} + + +void Logger::LogDebug(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + PerformLog(GetSessionId(), DebugLog, GetName(), GetThreadId(), fmt, args); + va_end(args); +} + + +void Logger::LogInfo(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + PerformLog(GetSessionId(), InfoLog, GetName(), GetThreadId(), fmt, args); + va_end(args); +} + + +void Logger::LogWarn(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + PerformLog(GetSessionId(), WarningLog, GetName(), GetThreadId(), fmt, args); + va_end(args); +} + + +void Logger::LogError(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + PerformLog(GetSessionId(), ErrorLog, GetName(), GetThreadId(), fmt, args); + va_end(args); +} + + +void Logger::LogAlert(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + PerformLog(GetSessionId(), AlertLog, GetName(), GetThreadId(), fmt, args); + va_end(args); +} + + +string Logger::GetName() +{ + char* name = BNLoggerGetName(m_object); + string result = name; + BNFreeString(name); + return result; +} + + +size_t Logger::GetSessionId() +{ + return BNLoggerGetSessionId(m_object); +} + + +Ref<Logger> LogRegistry::CreateLogger(const std::string& loggerName, size_t sessionId) +{ + return new Logger(BNLogCreateLogger(loggerName.c_str(), sessionId)); +} + + +Ref<Logger> LogRegistry::GetLogger(const std::string& loggerName, size_t sessionId) +{ + return new Logger(BNLogGetLogger(loggerName.c_str(), sessionId)); +} + + +vector<string> LogRegistry::GetLoggerNames() +{ + size_t count = 0; + char** names = BNLogGetLoggerNames(&count); + vector<string> result; + result.reserve(count); + for (size_t i = 0; i < count; ++i) + result.push_back(names[i]); + BNFreeStringList(names, count); + return result; +} + + +struct RegisterLoggerCallbackContext +{ + std::function<void(const string&)> func; +}; + + +static void RegisterLoggerCallbackHelper(const char* name, void* ctxt) +{ + RegisterLoggerCallbackContext* cb = (RegisterLoggerCallbackContext*)ctxt; + cb->func(name); +} + + +void LogRegistry::RegisterLoggerCallback(const std::function<void(const string&)>& cb) +{ + // we leak this LoggerCallback but since you can't unregister them it doesn't really matter + auto loggerCallback = new RegisterLoggerCallbackContext; + loggerCallback->func = cb; + BNLogRegisterLoggerCallback(RegisterLoggerCallbackHelper, loggerCallback); +} diff --git a/python/binaryview.py b/python/binaryview.py index b2df6ca2..3ebe79af 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -39,10 +39,10 @@ import binaryninja from . import _binaryninjacore as core from .enums import ( AnalysisState, SymbolType, Endianness, ModificationStatus, StringType, SegmentFlag, SectionSemantics, FindFlag, - TypeClass, BinaryViewEventType, FunctionGraphType, TagReferenceType, TagTypeType, RegisterValueType + TypeClass, BinaryViewEventType, FunctionGraphType, TagReferenceType, TagTypeType, RegisterValueType, LogLevel ) from . import associateddatastore # required for _BinaryViewAssociatedDataStore -from .log import log_error, log_warn +from .log import log, log_debug, log_info, log_warn, log_error, log_alert, Logger from . import typelibrary from . import fileaccessor from . import databuffer @@ -7499,6 +7499,9 @@ class BinaryView: def typed_data_accessor(self, address: int, type: '_types.Type') -> 'TypedDataAccessor': return TypedDataAccessor(type, address, self, self.endianness) + def create_logger(self, logger_name:str) -> Logger: + return Logger(self.file.session_id, logger_name) + class BinaryReader: """ diff --git a/python/filemetadata.py b/python/filemetadata.py index 5fde78d9..bd9a9954 100644 --- a/python/filemetadata.py +++ b/python/filemetadata.py @@ -171,6 +171,10 @@ class FileMetadata: def nav(self, value: NavigationHandler) -> None: self._nav = value + @property + def session_id(self) -> int: + return core.BNFileMetadataGetSessionId(self.handle) + @classmethod def _unregister(cls, f): handle = ctypes.cast(f, ctypes.c_void_p) diff --git a/python/function.py b/python/function.py index 2bd64f4a..b1887628 100644 --- a/python/function.py +++ b/python/function.py @@ -336,8 +336,9 @@ class Function: >>> current_function = bv.functions[0] >>> here = current_function.start """ - def __init__(self, view: Optional['binaryview.BinaryView'] = None, handle: Optional[core.BNFunctionHandle] = None): + def __init__(self, view: Optional['binaryview.BinaryView'] = None, handle:core.BNFunctionHandle=None): self._advanced_analysis_requests = 0 + self.handle = None assert handle is not None, "creation of standalone 'Function' objects is not implemented" FunctionHandle = ctypes.POINTER(core.BNFunction) self.handle = ctypes.cast(handle, FunctionHandle) @@ -349,7 +350,7 @@ class Function: self._platform = None def __del__(self): - if core is not None: + if core is not None and self.handle is not None: if self._advanced_analysis_requests > 0: core.BNReleaseAdvancedFunctionAnalysisDataMultiple(self.handle, self._advanced_analysis_requests) core.BNFreeFunction(self.handle) diff --git a/python/log.py b/python/log.py index 98aef1eb..d5a5f3e6 100644 --- a/python/log.py +++ b/python/log.py @@ -18,9 +18,12 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. +from typing import Optional, Union, Any + # Binary Ninja components from . import _binaryninjacore as core from .enums import LogLevel +import threading _output_to_log = False @@ -35,7 +38,7 @@ def is_output_redirected_to_log(): return _output_to_log -def log(level, text): +def log(level: LogLevel, text: Union[str, Any], logger: Optional[str]="", session:int=0): """ ``log`` writes messages to the log console for the given log level. @@ -55,10 +58,10 @@ def log(level, text): """ if not isinstance(text, str): text = str(text) - core.BNLogString(level, text) + core.BNLogString(session, level, logger, threading.current_thread().ident, text) -def log_debug(text): +def log_debug(text: Union[str, Any], logger: Optional[str]=""): """ ``log_debug`` Logs debugging information messages to the console. @@ -72,10 +75,10 @@ def log_debug(text): """ if not isinstance(text, str): text = str(text) - core.BNLogString(LogLevel.DebugLog, text) + core.BNLogString(0, LogLevel.DebugLog, logger, threading.current_thread().ident, text) -def log_info(text): +def log_info(text: Union[str, Any], logger: Optional[str]=""): """ ``log_info`` Logs general information messages to the console. @@ -89,10 +92,10 @@ def log_info(text): """ if not isinstance(text, str): text = str(text) - core.BNLogString(LogLevel.InfoLog, text) + core.BNLogString(0, LogLevel.InfoLog, logger, threading.current_thread().ident, text) -def log_warn(text): +def log_warn(text: Union[str, Any], logger: Optional[str]=""): """ ``log_warn`` Logs message to console, if run through the GUI it logs with **Warning** icon. @@ -107,10 +110,10 @@ def log_warn(text): """ if not isinstance(text, str): text = str(text) - core.BNLogString(LogLevel.WarningLog, text) + core.BNLogString(0, LogLevel.WarningLog, logger, threading.current_thread().ident, text) -def log_error(text): +def log_error(text: Union[str, Any], logger: Optional[str]=""): """ ``log_error`` Logs message to console, if run through the GUI it logs with **Error** icon, focusing the error console. @@ -125,10 +128,10 @@ def log_error(text): """ if not isinstance(text, str): text = str(text) - core.BNLogString(LogLevel.ErrorLog, text) + core.BNLogString(0, LogLevel.ErrorLog, logger, threading.current_thread().ident, text) -def log_alert(text): +def log_alert(text: Union[str, Any], logger: Optional[str]=""): """ ``log_alert`` Logs message console and to a pop up window if run through the GUI. @@ -143,10 +146,10 @@ def log_alert(text): """ if not isinstance(text, str): text = str(text) - core.BNLogString(LogLevel.AlertLog, text) + core.BNLogString(0, LogLevel.AlertLog, logger, threading.current_thread().ident, text) -def log_to_stdout(min_level=LogLevel.InfoLog): +def log_to_stdout(min_level: Optional[LogLevel]=LogLevel.InfoLog): """ ``log_to_stdout`` redirects minimum log level to standard out. @@ -163,7 +166,7 @@ def log_to_stdout(min_level=LogLevel.InfoLog): core.BNLogToStdout(min_level) -def log_to_stderr(min_level): +def log_to_stderr(min_level: LogLevel): """ ``log_to_stderr`` redirects minimum log level to standard error. @@ -173,7 +176,7 @@ def log_to_stderr(min_level): core.BNLogToStderr(min_level) -def log_to_file(min_level, path, append=False): +def log_to_file(min_level: LogLevel, path: str, append: bool=False): """ ``log_to_file`` redirects minimum log level to a file named ``path``, optionally appending rather than overwriting. @@ -192,3 +195,28 @@ def close_logs(): :rtype: None """ core.BNCloseLogs() + + +class Logger: + def __init__(self, session_id:int, logger_name:str): + self.session_id = session_id + self.logger_name = logger_name + self.handle = core.BNLogCreateLogger(logger_name, session_id) + + def log(self, level:LogLevel, message:str) -> None: + log(level, message, self.logger_name, self.session_id) + + def log_debug(self, message:str) -> None: + log(LogLevel.DebugLog, message, self.logger_name, self.session_id) + + def log_info(self, message:str) -> None: + log(LogLevel.InfoLog, message, self.logger_name, self.session_id) + + def log_warn(self, message:str) -> None: + log(LogLevel.WarningLog, message, self.logger_name, self.session_id) + + def log_error(self, message:str) -> None: + log(LogLevel.ErrorLog, message, self.logger_name, self.session_id) + + def log_alert(self, message:str) -> None: + log(LogLevel.AlertLog, message, self.logger_name, self.session_id)
\ No newline at end of file diff --git a/python/types.py b/python/types.py index 62cc0154..2d29ac2d 100644 --- a/python/types.py +++ b/python/types.py @@ -2242,11 +2242,13 @@ class EnumerationType(IntegerType): count = ctypes.c_ulonglong() members = core.BNGetEnumerationMembers(self.enum_handle, count) assert members is not None, "core.BNGetEnumerationMembers returned None" - result = [] - for i in range(0, count.value): - result.append(EnumerationMember(members[i].name, members[i].value if not members[i].isDefault else None)) - core.BNFreeEnumerationMemberList(members, count.value) - return result + try: + result = [] + for i in range(0, count.value): + result.append(EnumerationMember(members[i].name, members[i].value if not members[i].isDefault else None)) + return result + finally: + core.BNFreeEnumerationMemberList(members, count.value) @classmethod def create( diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 966f3ff2..da9f5bee 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -129,7 +129,7 @@ pub mod logger { if let Ok(msg) = CString::new(format!("{}", record.args())) { unsafe { - BNLog(level, msg.as_ptr()); + BNLog(0, level, std::ptr::null(), 0, msg.as_ptr()); } }; } @@ -145,7 +145,7 @@ pub mod logger { } pub trait LogListener: 'static + Sync { - fn log(&self, level: Level, msg: &BnStr); + fn log(&self, session: usize, level: Level, msg: &BnStr, logger_name: &BnStr, tid: usize); fn level(&self) -> Level; fn close(&self) {} } @@ -193,13 +193,13 @@ pub mod logger { LogGuard { ctxt: raw } } - extern "C" fn cb_log<L>(ctxt: *mut c_void, level: Level, msg: *const c_char) + extern "C" fn cb_log<L>(ctxt: *mut c_void, session: usize, level: Level, msg: *const c_char, logger_name: *const c_char, tid: usize) where L: LogListener, { ffi_wrap!("LogListener::log", unsafe { let listener = &*(ctxt as *const L); - listener.log(level, BnStr::from_raw(msg)); + listener.log(session, level, BnStr::from_raw(msg), BnStr::from_raw(logger_name), tid); }) } diff --git a/suite/api_test.py b/suite/api_test.py index 6c4bc493..ea8378a8 100644 --- a/suite/api_test.py +++ b/suite/api_test.py @@ -1507,7 +1507,7 @@ class TestWithFunction(TestWithBinaryView): assert mlil == self.func.mlil assert mlil == self.func.mlil_if_available - mmlil = self.func.mapped_medium_level_il + mmlil = self.func.llil.mapped_medium_level_il assert isinstance(mmlil, MediumLevelILFunction) assert mmlil == self.func.mmlil assert mmlil == self.func.mmlil_if_available diff --git a/ui/filecontext.h b/ui/filecontext.h index ef98d6a9..37d2ff79 100644 --- a/ui/filecontext.h +++ b/ui/filecontext.h @@ -4,6 +4,7 @@ #include <map> #include <set> #include <string> +#include <ctime> #include "binaryninjaapi.h" #include "uicontext.h" diff --git a/ui/logview.h b/ui/logview.h index 2a83d919..ed99e24a 100644 --- a/ui/logview.h +++ b/ui/logview.h @@ -7,6 +7,10 @@ #include <QtWidgets/QStyledItemDelegate> #include <QtWidgets/QLabel> #include <QtWidgets/QToolButton> +#include <QtWidgets/QComboBox> +#include <QtWidgets/QLineEdit> +#include <QtCore/QSortFilterProxyModel> +#include <QtWidgets/QStackedWidget> #include <mutex> #include <string> #include <utility> @@ -15,6 +19,7 @@ #include "binaryninjaapi.h" #include "action.h" #include "globalarea.h" +#include "render.h" #define LOG_UPDATE_INTERVAL 100 @@ -24,12 +29,41 @@ class ViewFrame; struct BINARYNINJAUIAPI LogListItem { + size_t sessionId; BNLogLevel level; std::string text; bool selected; + std::string logger; + size_t threadId{0}; - LogListItem(BNLogLevel level, std::string text, bool selected = false) : - level(level), text(text), selected(selected) {}; + LogListItem(size_t sessionId, BNLogLevel level, std::string text, bool selected = false, const std::string& logger_name = "", size_t tid = 0); +}; + +enum LoggingScope +{ + CurrentTabOnly, + CurrentTabAndGlobal, + GlobalOnly, + AllTabs +}; + +class BINARYNINJAUIAPI LogListFilterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + QString m_loggerName; + size_t m_sessionId {0}; + LoggingScope m_scope; + + public: + LogListFilterProxyModel(QObject* parent); + virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; + virtual QVariant data(const QModelIndex& idx, int role) const override; + void setScope(LoggingScope scope); + LoggingScope getScope() const { return m_scope; } + public Q_SLOTS: + void updateSession(size_t sessionId); + void updateLogger(const QString & loggerName); + void updateFilter(); }; @@ -40,34 +74,62 @@ class BINARYNINJAUIAPI LogListModel : public QAbstractItemModel, public BinaryNi QWidget* m_owner; std::deque<LogListItem> m_items; std::deque<LogListItem> m_visibleItems; - int64_t m_maxSize; std::vector<LogListItem> m_pendingItems; std::mutex m_mutex; std::mutex m_pendingMutex; + std::string m_logger; + size_t m_sessionId {0}; + + bool m_showSessionId {false}; + bool m_showThreadId {false}; + bool m_showLoggerName {false}; + bool m_showLogLevel {false}; - public: - LogListModel(QWidget* parent); - ~LogListModel(); + public: + static constexpr int Level = Qt::UserRole + 1; + static constexpr int Logger = Qt::UserRole + 2; + static constexpr int ThreadId = Qt::UserRole + 3; + static constexpr int Message = Qt::UserRole + 4; + static constexpr int Session = Qt::UserRole + 5; + static constexpr int FormattedMessage = Qt::UserRole + 6; - void addPendingItems(); - void clear(); - std::vector<LogListItem> getSelectedItems(); - bool hasSelectedItems(); + LogListModel(QWidget* parent); + ~LogListModel(); - virtual void LogMessage(BNLogLevel level, const std::string& msg) override; - virtual BNLogLevel GetLogLevel() override; + void addPendingItems(); + void clear(); + std::vector<LogListItem> getSelectedItems(); + bool hasSelectedItems(); - virtual QModelIndex index(int row, int col, const QModelIndex& parent) const override; - virtual QModelIndex parent(const QModelIndex& i) const override; - virtual bool hasChildren(const QModelIndex& parent) const override; - virtual int rowCount(const QModelIndex& parent) const override; - virtual int columnCount(const QModelIndex& parent) const override; - virtual QVariant data(const QModelIndex& i, int role) const override; - virtual bool setData(const QModelIndex& i, const QVariant& value, int role) override; + virtual void LogMessage(size_t sessionId, BNLogLevel level, const std::string& msg, const std::string& loggerName = "", size_t tid = 0) override; + virtual BNLogLevel GetLogLevel() override; - public Q_SLOTS: - void notifyDataChanged(); + virtual QModelIndex index(int row, int col, const QModelIndex& parent) const override; + virtual QModelIndex parent(const QModelIndex& i) const override; + virtual bool hasChildren(const QModelIndex& parent) const override; + virtual int rowCount(const QModelIndex& parent) const override; + virtual int columnCount(const QModelIndex& parent) const override; + virtual QVariant data(const QModelIndex& i, int role) const override; + virtual bool setData(const QModelIndex& i, const QVariant& value, int role) override; + + void setDisplaySessionId(bool value); + void setDisplayThreadId(bool value); + void setDisplayLoggerName(bool value); + void setDisplayLogLevel(bool value); + void setMinLogLevel(BNLogLevel level); + void setMaxLogLength(size_t length); + + size_t getSessionId() const { return m_sessionId; } + bool getDisplaySessionId() const { return m_showSessionId; } + bool getDisplayThreadId() const { return m_showThreadId; } + bool getDisplayLoggerName() const { return m_showLoggerName; } + bool getDisplayLogLevel() const { return m_showLogLevel; } + Q_SIGNALS: + void settingsUpdated(); + + public Q_SLOTS: + void notifySessionChanged(size_t sessionId); }; @@ -85,22 +147,22 @@ class BINARYNINJAUIAPI LogItemDelegate : public QStyledItemDelegate bool IsNavigable(const QString& str, const std::pair<int, int>& offsetLen, uint64_t& value, bool highlight) const; - public: - LogItemDelegate(QWidget* parent); + public: + LogItemDelegate(QWidget* parent); - void updateFonts(); - virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& idx) const override; - virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& idx) const override; + void updateFonts(); + virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& idx) const override; + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& idx) const override; - protected: - bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, - const QModelIndex& index) override; + protected: + bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, + const QModelIndex& index) override; - Q_SIGNALS: - void notifyDataChanged(); + Q_SIGNALS: + void notifySessionChanged(size_t sessionId); - public Q_SLOTS: - void viewChanged(QWidget* frame); + public Q_SLOTS: + void viewChanged(QWidget* frame); }; @@ -112,47 +174,75 @@ class BINARYNINJAUIAPI LogView : public GlobalAreaWidget std::vector<std::pair<QAction*, bool>> m_actionEnableList; QListView* m_list; LogListModel* m_listModel; + LogListFilterProxyModel* m_model; LogItemDelegate* m_itemDelegate; QTimer* m_updateTimer; + QComboBox* m_comboBox; + QLineEdit* m_lineEdit; + QWidget* m_filterWidget; + RenderContext m_render; + bool m_doClear; bool m_scrolledToEnd; bool m_hasSelection = false; - public: - LogView(LogStatus* logStatus); + // bool m_subSelectionMode; + // size_t m_baseSelectionIndex; + // size_t m_baseSelectionOffset; + // size_t m_currentSelectionIndex; + // size_t m_currentSelectionOffset; + // size_t m_visibleRows {1}; + // size_t m_topLine {0}; + // size_t m_selectCount {0}; + + public: + LogView(LogStatus* logStatus); + void adjustSize(int width, int height); - virtual void copy(); - virtual bool canCopy(); + virtual void copy(); + virtual bool canCopy(); - static void SetLogLevel(BNLogLevel level); - static void SetLogSize(size_t maxSize); - static bool IsHexString(const QString& str, std::pair<int, int> offsetLen); - static bool StartsWith0x(const QString& str, std::pair<int, int> offsetLen); + static void setLogLevel(BNLogLevel level); + static void setLogSize(size_t maxSize); + static bool IsHexString(const QString& str, std::pair<int, int> offsetLen); + static bool StartsWith0x(const QString& str, std::pair<int, int> offsetLen); - void notifyFontChanged() override; - void notifyThemeChanged() override; - void notifyViewChanged(ViewFrame* frame) override; - void focus() override; + void notifyFontChanged() override; + void notifyThemeChanged() override; + void notifyViewChanged(ViewFrame* frame) override; + void focus() override; - LogListModel* model() { return m_listModel; } + LogListModel* model() { return m_listModel; } + void updateFilter(const QString& filterText); + LoggingScope getScope() const { return m_model->getScope(); } - protected: - void contextMenuEvent(QContextMenuEvent* event) override; + // std::pair<size_t, size_t> GetSelectionIndexAndOffsetFromPosition(const QPoint& position) const; + // virtual void mousePressEvent(QMouseEvent* event) override; + // virtual void mouseReleaseEvent(QMouseEvent* event) override; + // virtual void mouseMoveEvent(QMouseEvent* event) override; - Q_SIGNALS: - void notifyUiStatus(); - void viewChanged(QWidget* frame); + // bool IsInSubSelectionMode() const { return m_subSelectionMode; } + // std::pair<size_t, size_t> GetSelectionIndicies() const { return {m_baseSelectionOffset, m_currentSelectionOffset}; } - public Q_SLOTS: - void clear(); + protected: + void contextMenuEvent(QContextMenuEvent* event) override; + virtual void resizeEvent(QResizeEvent* event) override; - private Q_SLOTS: - void scrollRangeChanged(int minimum, int maximum); - void scrollValueChanged(int value); - void updateSelection(const QItemSelection& selected, const QItemSelection& deselected); - void updateTimerEvent(); - void updateUiStatus(); + Q_SIGNALS: + void notifyUiStatus(); + void viewChanged(QWidget* frame); + + public Q_SLOTS: + void clear(); + + private Q_SLOTS: + void scrollRangeChanged(int minimum, int maximum); + void scrollValueChanged(int value); + void updateSelection(const QItemSelection& selected, const QItemSelection& deselected); + void updateTimerEvent(); + void updateUiStatus(); + void showContextMenu(); }; @@ -162,19 +252,30 @@ class BINARYNINJAUIAPI LogStatus : public QWidget QToolButton* m_errorIndicator; QToolButton* m_warnIndicator; + ContextMenuManager* m_contextMenuManager; + QMenu* m_menu; - int m_errorCount = 0; - int m_warnCount = 0; - - public: - LogStatus(QWidget* parent); - - void incrementErrorCount(int count); - void incrementWarningCount(int count); - void clearIndicators(); + std::mutex m_countMutex; + int m_totalErrorCount = 0; + int m_totalWarnCount = 0; + std::map<uint64_t, int> m_sessionErrorCount; + std::map<uint64_t, int> m_sessionWarnCount; + size_t m_sessionId {0}; + LogView* m_logView; + std::map<size_t, QString> getSessionToNameMap(); - void updateTheme(); + public: + LogStatus(QWidget* parent); + void setLogView(LogView* view) { m_logView = view; } + void incrementErrorCount(uint64_t session, int count); + void incrementWarningCount(uint64_t session, int count); + void clearIndicators(bool warnings, bool errors); + void checkForErrors(); + void focusTab(QString tabName); - private Q_SLOTS: - void clearStatus(); + void updateTheme(); + public Q_SLOTS: + void notifySessionChanged(size_t sessionId); + private Q_SLOTS: + void clearStatus(bool error); }; diff --git a/ui/uicontext.h b/ui/uicontext.h index 44c1e53d..94b05b05 100644 --- a/ui/uicontext.h +++ b/ui/uicontext.h @@ -339,6 +339,8 @@ class BINARYNINJAUIAPI UIContext */ virtual QWidget* getCurrentTab() = 0; + virtual QWidget* createNewTab(bool focus = true) = 0; + /*! Get the current View associated with the given QWidget, if it exists \param tab QWidget which could be a ViewFrame diff --git a/ui/viewframe.h b/ui/viewframe.h index 00ec700a..8d123f2b 100644 --- a/ui/viewframe.h +++ b/ui/viewframe.h @@ -96,6 +96,14 @@ class TransformParameterDialog; class ViewPaneHeaderSubtypeWidget; // struct BinaryNinjaCore::LinearDisassemblyLine; +class View; +class InitialNavigation: public BinaryNinja::BinaryDataNotification +{ + View* m_view; + public: + InitialNavigation(View* view); + virtual void OnSymbolAdded(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* symbol) override; +}; class BINARYNINJAUIAPI View { @@ -343,6 +351,7 @@ class BINARYNINJAUIAPI ViewFrame : public QWidget bool m_graphViewPreferred = false; std::vector<QString> m_viewTypePriority; int m_preferredSyncGroup = 1; + InitialNavigation* m_initialNavigation; UIActionHandler m_actionHandler; @@ -487,6 +496,7 @@ class BINARYNINJAUIAPI ViewFrame : public QWidget void forceSyncFromView(); ViewFrame* getOtherPane(); + void UnRegisterInitialNavigation(); public Q_SLOTS: virtual void assemble(); |
