Merge branch 'dev' into Rohan-branch

This commit is contained in:
Rohan Sagar 2024-04-22 15:23:44 -04:00 committed by GitHub
commit 3c7a987b0d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 268 additions and 350 deletions

View File

@ -1,4 +1,4 @@
version: '2.2.0-git-{build}'
version: '2.4.0-git-{build}'
image: 'Visual Studio 2017'
clone_depth: 1

View File

@ -36,7 +36,7 @@ if(NOT CUTTER_ENABLE_PYTHON)
endif()
set(CUTTER_VERSION_MAJOR 2)
set(CUTTER_VERSION_MINOR 2)
set(CUTTER_VERSION_MINOR 4)
set(CUTTER_VERSION_PATCH 0)
set(CUTTER_VERSION "${CUTTER_VERSION_MAJOR}.${CUTTER_VERSION_MINOR}.${CUTTER_VERSION_PATCH}")

View File

@ -57,12 +57,11 @@ endif()
# TODO: This version number should be fetched automatically
# instead of being hardcoded.
set (Rizin_VERSION 0.7)
set (Rizin_VERSION 0.8)
set (RZ_LIBS rz_core rz_config rz_cons rz_io rz_util rz_flag rz_asm rz_debug
rz_hash rz_bin rz_lang rz_il rz_analysis rz_parse rz_bp rz_egg rz_reg
rz_search rz_syscall rz_socket rz_magic rz_crypto rz_type rz_diff rz_sign
rz_demangler)
set (RZ_LIBS rz_core rz_config rz_cons rz_io rz_util rz_flag rz_arch rz_debug
rz_hash rz_bin rz_lang rz_il rz_bp rz_egg rz_reg rz_search rz_syscall
rz_socket rz_magic rz_crypto rz_type rz_diff rz_sign rz_demangler)
set (RZ_EXTRA_LIBS rz_main)
set (RZ_BIN rz-bin rizin rz-diff rz-find rz-gg rz-hash rz-run rz-asm rz-ax)

11
dist/CMakeLists.txt vendored
View File

@ -24,7 +24,9 @@ if(WIN32)
install(CODE "
set(ENV{RZ_PREFIX} \"\${CMAKE_INSTALL_PREFIX}\")
set(ENV{PATH} \"\${CMAKE_INSTALL_PREFIX};\$ENV{PATH}\")
execute_process(COMMAND powershell \"${CMAKE_CURRENT_SOURCE_DIR}/bundle_jsdec.ps1\" \"\${CMAKE_INSTALL_PREFIX}\"
execute_process(COMMAND powershell \"${CMAKE_CURRENT_SOURCE_DIR}/bundle_jsdec.ps1\"
\"\${CMAKE_INSTALL_PREFIX}\"
\"-DCUTTER_INSTALL_PLUGDIR=plugins/native\"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE SCRIPT_RESULT)
if (SCRIPT_RESULT)
@ -124,9 +126,14 @@ endif()
if(CUTTER_ENABLE_DEPENDENCY_DOWNLOADS AND (NOT WIN32))
if (CUTTER_PACKAGE_JSDEC)
if(APPLE)
set (JSDEC_PLUGIN_OPTIONS "-DCUTTER_INSTALL_PLUGDIR=plugins/native")
else()
set (JSDEC_PLUGIN_OPTIONS "")
endif()
install(CODE "
execute_process(COMMAND \"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/jsdec.sh\"
--pkg-config-path=\${CMAKE_INSTALL_PREFIX}/lib/pkgconfig --prefix=\${CMAKE_INSTALL_PREFIX}
\"\${CMAKE_INSTALL_PREFIX}\" \"${JSDEC_PLUGIN_OPTIONS}\"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE SCRIPT_RESULT)
if (SCRIPT_RESULT)

24
dist/bundle_jsdec.ps1 vendored
View File

@ -2,16 +2,26 @@ $dist = $args[0]
$python = Split-Path((Get-Command python.exe).Path)
if (-not (Test-Path -Path 'jsdec' -PathType Container)) {
git clone https://github.com/rizinorg/jsdec.git --depth 1 --branch "v0.7.0"
git clone https://github.com/rizinorg/jsdec.git --depth 1 --branch "dev"
}
cd jsdec
& meson.exe --buildtype=release --prefix="$dist" build
ninja -C build install
$jsdecdir = (Get-Item .).FullName
& meson.exe setup --buildtype=release -Dbuild_type=cutter "$jsdecdir\build_lib"
ninja -C "$jsdecdir\build_lib"
# cmake is silly and expects .lib but meson generates the static lib as .a
Copy-Item "$jsdecdir\build_lib\libjsdec.a" -Destination "$jsdecdir\build_lib\jsdec.lib"
mkdir build_plugin
cd build_plugin
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DJSDEC_BUILD_DIR="$jsdecdir\build_lib" -DCMAKE_INSTALL_PREFIX="$dist" $cmake_opts "$jsdecdir\cutter-plugin"
ninja install
$ErrorActionPreference = 'Stop'
$pathdll = "$dist\lib\rizin\plugins\core_pdd.dll"
$pathdll = "$dist\share\rizin\cutter\plugins\native\jsdec_cutter.dll"
if(![System.IO.File]::Exists($pathdll)) {
type build\meson-logs\meson-log.txt
ls "$dist\lib\rizin\plugins\"
echo "files: $dist\share\rizin\cutter\plugins\native\"
ls "$dist\share\rizin\cutter\plugins\native\"
throw (New-Object System.IO.FileNotFoundException("File not found: $pathdll", $pathdll))
}
Remove-Item -Recurse -Force "$dist\lib\rizin\plugins\core_pdd.lib"

View File

@ -3,7 +3,7 @@ $cmake_opts = $args[1]
$python = Split-Path((Get-Command python.exe).Path)
if (-not (Test-Path -Path 'rz_libyara' -PathType Container)) {
git clone https://github.com/rizinorg/rz-libyara.git --depth 1 rz_libyara
git clone https://github.com/rizinorg/rz-libyara.git --depth 1 --branch main rz_libyara
git -C rz_libyara submodule init
git -C rz_libyara submodule update
}

View File

@ -24,9 +24,9 @@ copyright = '2020, The Cutter Developers'
author = 'The Cutter Developers'
# The short X.Y version
version = '2.2'
version = '2.4'
# The full version, including a2lpha/beta/rc tags
release = '2.2.0'
release = '2.4.0'
# -- General configuration ---------------------------------------------------

2
rizin

@ -1 +1 @@
Subproject commit fa455f8b5244ad0cebe2fa8aca1c71096f55dfa1
Subproject commit 34f1a9e7b40e289cdf8e7f03c145bdbd5d41dc89

View File

@ -1,20 +1,24 @@
#!/bin/bash
set -e
INSTALL_PREFIX="$1"
EXTRA_CMAKE_OPTS="$2"
SCRIPTPATH=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
cd "$SCRIPTPATH/.."
if [ ! -d jsdec ]; then
git clone https://github.com/rizinorg/jsdec.git --depth 1 --branch "v0.7.0"
git clone https://github.com/rizinorg/jsdec.git --depth 1 --branch "dev"
fi
cd jsdec
if [ -d build ]; then
rm -rf build
if [ -d build_lib ]; then
rm -rf build_lib
fi
meson --buildtype=release "$@" build
ninja -C build
ninja -C build install
meson setup --buildtype=release --pkg-config-path="$INSTALL_PREFIX/lib/pkgconfig" -Dbuild_type=cutter build_lib
ninja -C build_lib
mkdir build_plugin && cd build_plugin
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DJSDEC_BUILD_DIR="../build_lib" -DCMAKE_INSTALL_PREFIX="$INSTALL_PREFIX" $EXTRA_CMAKE_OPTS ../cutter-plugin
ninja install

View File

@ -8,14 +8,14 @@ EXTRA_CMAKE_OPTS="$2"
cd "$SCRIPTPATH/.."
if [[ ! -d rz_libyara ]]; then
git clone https://github.com/rizinorg/rz-libyara.git --depth 1 rz_libyara
git clone https://github.com/rizinorg/rz-libyara.git --depth 1 --branch main rz_libyara
git -C rz_libyara submodule init
git -C rz_libyara submodule update
fi
cd rz_libyara
meson --buildtype=release --pkg-config-path="$INSTALL_PREFIX/lib/pkgconfig" --prefix="$INSTALL_PREFIX" -Duse_sys_yara=disabled build
meson --buildtype=release --pkg-config-path="$INSTALL_PREFIX/lib/pkgconfig" --prefix="$INSTALL_PREFIX" -Denable_openssl=false -Duse_sys_yara=disabled build
ninja -C build install
cd cutter-plugin

View File

@ -120,10 +120,6 @@ CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc
Config()->setOutputRedirectionEnabled(clOptions.outputRedirectionEnabled);
if (JSDecDecompiler::isAvailable()) {
Core()->registerDecompiler(new JSDecDecompiler(Core()));
}
#if CUTTER_RZGHIDRA_STATIC
Core()->registerDecompiler(new RzGhidraDecompiler(Core()));
#endif

View File

@ -10,7 +10,8 @@ void openIssue()
{
RzCoreLocked core(Core());
RzBinFile *bf = rz_bin_cur(core->bin);
RzBinInfo *info = rz_bin_get_info(core->bin);
RzBinObject *bobj = rz_bin_cur_object(core->bin);
const RzBinInfo *info = bobj ? rz_bin_object_get_info(bobj) : nullptr;
RzBinPlugin *plugin = rz_bin_file_cur_plugin(bf);
QString url, osInfo, format, arch, type;

View File

@ -10,107 +10,8 @@ Decompiler::Decompiler(const QString &id, const QString &name, QObject *parent)
{
}
static char *jsonToStrdup(const CutterJson &str)
{
const RzJson *j = str.lowLevelValue();
if (!j || j->type != RZ_JSON_STRING) {
return NULL;
}
return rz_str_dup(j->str_value);
}
static RzAnnotatedCode *parseJsonCode(CutterJson &json)
{
char *raw_code = jsonToStrdup(json["code"]);
if (!raw_code) {
return NULL;
}
RzAnnotatedCode *code = rz_annotated_code_new(raw_code);
if (!code) {
return NULL;
}
for (const auto &jsonAnnotation : json["annotations"]) {
RzCodeAnnotation annotation = {};
annotation.start = jsonAnnotation["start"].toUt64();
annotation.end = jsonAnnotation["end"].toUt64();
QString type = jsonAnnotation["type"].toString();
if (type == "offset") {
annotation.type = RZ_CODE_ANNOTATION_TYPE_OFFSET;
annotation.offset.offset = jsonAnnotation["offset"].toString().toULongLong();
} else if (type == "function_name") {
annotation.type = RZ_CODE_ANNOTATION_TYPE_FUNCTION_NAME;
annotation.reference.name = jsonToStrdup(jsonAnnotation["name"]);
annotation.reference.offset = jsonAnnotation["offset"].toString().toULongLong();
} else if (type == "global_variable") {
annotation.type = RZ_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE;
annotation.reference.offset = jsonAnnotation["offset"].toString().toULongLong();
} else if (type == "constant_variable") {
annotation.type = RZ_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE;
annotation.reference.offset = jsonAnnotation["offset"].toString().toULongLong();
} else if (type == "local_variable") {
annotation.type = RZ_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE;
annotation.variable.name = jsonToStrdup(jsonAnnotation["name"]);
} else if (type == "function_parameter") {
annotation.type = RZ_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER;
annotation.variable.name = jsonToStrdup(jsonAnnotation["name"]);
} else if (type == "syntax_highlight") {
annotation.type = RZ_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT;
QString highlightType = jsonAnnotation["syntax_highlight"].toString();
if (highlightType == "keyword") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_KEYWORD;
} else if (highlightType == "comment") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_COMMENT;
} else if (highlightType == "datatype") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_DATATYPE;
} else if (highlightType == "function_name") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_NAME;
} else if (highlightType == "function_parameter") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_PARAMETER;
} else if (highlightType == "local_variable") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_LOCAL_VARIABLE;
} else if (highlightType == "constant_variable") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_CONSTANT_VARIABLE;
} else if (highlightType == "global_variable") {
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_GLOBAL_VARIABLE;
}
}
rz_annotated_code_add_annotation(code, &annotation);
}
return code;
}
RzAnnotatedCode *Decompiler::makeWarning(QString warningMessage)
{
std::string temporary = warningMessage.toStdString();
return rz_annotated_code_new(strdup(temporary.c_str()));
}
JSDecDecompiler::JSDecDecompiler(QObject *parent) : Decompiler("jsdec", "jsdec", parent)
{
task = nullptr;
}
bool JSDecDecompiler::isAvailable()
{
return Core()->getConfigVariableSpaces().contains("jsdec");
}
void JSDecDecompiler::decompileAt(RVA addr)
{
if (task) {
return;
}
task = new RizinCmdTask("pddA @ " + QString::number(addr));
connect(task, &RizinCmdTask::finished, this, [this]() {
CutterJson json = task->getResultJson();
delete task;
task = nullptr;
if (!json.size()) {
emit finished(Decompiler::makeWarning(tr("Failed to parse JSON from jsdec")));
return;
}
RzAnnotatedCode *code = parseJsonCode(json);
emit finished(code);
});
task->startTask();
}

View File

@ -37,20 +37,4 @@ signals:
void finished(RzAnnotatedCode *codeDecompiled);
};
class JSDecDecompiler : public Decompiler
{
Q_OBJECT
private:
RizinCmdTask *task;
public:
explicit JSDecDecompiler(QObject *parent = nullptr);
void decompileAt(RVA addr) override;
bool isRunning() override { return task != nullptr; }
static bool isAvailable();
};
#endif // DECOMPILER_H

View File

@ -62,7 +62,7 @@ CutterJson RizinCmdTask::getResultJson()
}
char *copy = static_cast<char *>(rz_mem_alloc(strlen(res) + 1));
strcpy(copy, res);
return Core()->parseJson(copy, nullptr);
return Core()->parseJson("task", copy, nullptr);
}
const char *RizinCmdTask::getResultRaw()

View File

@ -273,6 +273,7 @@ void CutterCore::loadCutterRC()
}
qInfo() << tr("Loading initialization file from ") << cutterRCFilePath;
rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());
rz_cons_flush();
}
}
@ -286,6 +287,7 @@ void CutterCore::loadDefaultCutterRC()
}
qInfo() << tr("Loading initialization file from ") << cutterRCFilePath;
rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());
rz_cons_flush();
}
QList<QString> CutterCore::sdbList(QString path)
@ -473,19 +475,7 @@ QString CutterCore::cmdRaw(const char *cmd)
{
QString res;
CORE_LOCK();
rz_cons_push();
// rz_core_cmd does not return the output of the command
rz_core_cmd(core, cmd, 0);
// we grab the output straight from rz_cons
res = rz_cons_get_buffer();
// cleaning up
rz_cons_pop();
rz_cons_echo(NULL);
return res;
return rz_core_cmd_str(core, cmd);
}
CutterJson CutterCore::cmdj(const char *str)
@ -496,7 +486,7 @@ CutterJson CutterCore::cmdj(const char *str)
res = rz_core_cmd_str(core, str);
}
return parseJson(res, str);
return parseJson("cmdj", res, str);
}
QString CutterCore::cmdTask(const QString &str)
@ -507,9 +497,9 @@ QString CutterCore::cmdTask(const QString &str)
return task.getResult();
}
CutterJson CutterCore::parseJson(char *res, const char *cmd)
CutterJson CutterCore::parseJson(const char *name, char *res, const char *cmd)
{
if (!res) {
if (RZ_STR_ISEMPTY(res)) {
return CutterJson();
}
@ -517,24 +507,17 @@ CutterJson CutterCore::parseJson(char *res, const char *cmd)
if (!doc) {
if (cmd) {
eprintf("Failed to parse JSON for command \"%s\"\n", cmd);
RZ_LOG_ERROR("%s: Failed to parse JSON for command \"%s\"\n%s\n", name, cmd, res);
} else {
eprintf("Failed to parse JSON\n");
}
const int MAX_JSON_DUMP_SIZE = 8 * 1024;
size_t originalSize = strlen(res);
if (originalSize > MAX_JSON_DUMP_SIZE) {
res[MAX_JSON_DUMP_SIZE] = 0;
eprintf("%zu bytes total: %s ...\n", originalSize, res);
} else {
eprintf("%s\n", res);
RZ_LOG_ERROR("%s: Failed to parse JSON %s\n", name, res);
}
RZ_LOG_ERROR("%s: %s\n", name, res);
}
return CutterJson(doc, QSharedPointer<CutterJsonOwner>::create(doc, res));
}
QStringList CutterCore::autocomplete(const QString &cmd, RzLinePromptType promptType, size_t limit)
QStringList CutterCore::autocomplete(const QString &cmd, RzLinePromptType promptType)
{
RzLineBuffer buf;
int c = snprintf(buf.data, sizeof(buf.data), "%s", cmd.toUtf8().constData());
@ -543,18 +526,17 @@ QStringList CutterCore::autocomplete(const QString &cmd, RzLinePromptType prompt
}
buf.index = buf.length = std::min((int)(sizeof(buf.data) - 1), c);
RzLineCompletion completion;
rz_line_completion_init(&completion, limit);
rz_core_autocomplete(core(), &completion, &buf, promptType);
RzLineNSCompletionResult *compr = rz_core_autocomplete_rzshell(core(), &buf, promptType);
QStringList r;
r.reserve(rz_pvector_len(&completion.args));
for (size_t i = 0; i < rz_pvector_len(&completion.args); i++) {
auto optslen = rz_pvector_len(&compr->options);
r.reserve(optslen);
for (size_t i = 0; i < optslen; i++) {
r.push_back(QString::fromUtf8(
reinterpret_cast<const char *>(rz_pvector_at(&completion.args, i))));
reinterpret_cast<const char *>(rz_pvector_at(&compr->options, i))));
}
rz_line_ns_completion_result_free(compr);
rz_line_completion_fini(&completion);
return r;
}
@ -602,8 +584,6 @@ bool CutterCore::loadFile(QString path, ut64 baddr, ut64 mapaddr, int perms, int
rz_bin_select_idx(core->bin, NULL, idx);
}
#endif
} else {
// Not loading RzBin info coz va = false
}
auto iod = core->io ? core->io->desc : NULL;
@ -611,7 +591,8 @@ bool CutterCore::loadFile(QString path, ut64 baddr, ut64 mapaddr, int perms, int
core->file && iod && (core->file->fd == iod->fd) && iod->plugin && iod->plugin->isdbg;
if (!debug && rz_flag_get(core->flags, "entry0")) {
rz_core_cmd0(core, "s entry0");
ut64 addr = rz_num_math(core->num, "entry0");
rz_core_seek_and_save(core, addr, true);
}
if (perms & RZ_PERM_W) {
@ -621,6 +602,7 @@ bool CutterCore::loadFile(QString path, ut64 baddr, ut64 mapaddr, int perms, int
}
}
rz_cons_flush();
fflush(stdout);
return true;
}
@ -715,19 +697,16 @@ void CutterCore::delFlag(const QString &name)
emit flagsChanged();
}
PRzAnalysisBytes CutterCore::getRzAnalysisBytesSingle(RVA addr)
CutterRzIter<RzAnalysisBytes> CutterCore::getRzAnalysisBytesSingle(RVA addr)
{
CORE_LOCK();
ut8 buf[128];
rz_io_read_at(core->io, addr, buf, sizeof(buf));
auto seek = seekTemp(addr);
auto vec = fromOwned(rz_core_analysis_bytes(core, addr, buf, sizeof(buf), 1));
auto ab = vec && rz_pvector_len(vec.get()) > 0
? reinterpret_cast<RzAnalysisBytes *>(rz_pvector_pop_front(vec.get()))
: nullptr;
return { ab, rz_analysis_bytes_free };
// Warning! only safe to use with stack buffer, due to instruction count being 1
auto result =
CutterRzIter<RzAnalysisBytes>(rz_core_analysis_bytes(core, addr, buf, sizeof(buf), 1));
return result;
}
QString CutterCore::getInstructionBytes(RVA addr)
@ -1027,18 +1006,10 @@ RVA CutterCore::nextOpAddr(RVA startAddr, int count)
{
CORE_LOCK();
auto seek = seekTemp(startAddr);
auto vec = fromOwned(rz_core_analysis_bytes(core, core->offset, core->block,
(int)core->blocksize, count + 1));
auto consumed =
rz_core_analysis_ops_size(core, core->offset, core->block, (int)core->blocksize, count);
RVA addr = startAddr + 1;
if (!vec) {
return addr;
}
auto ab = reinterpret_cast<RzAnalysisBytes *>(rz_pvector_tail(vec.get()));
if (!(ab && ab->op)) {
return addr;
}
addr = ab->op->addr;
RVA addr = startAddr + consumed;
return addr;
}
@ -1324,7 +1295,7 @@ RVA CutterCore::getLastFunctionInstruction(RVA addr)
if (!fcn) {
return RVA_INVALID;
}
RzAnalysisBlock *lastBB = (RzAnalysisBlock *)rz_list_last(fcn->bbs);
RzAnalysisBlock *lastBB = (RzAnalysisBlock *)rz_pvector_tail(fcn->bbs);
return lastBB ? rz_analysis_block_get_op_addr(lastBB, lastBB->ninstr - 1) : RVA_INVALID;
}
@ -1399,13 +1370,17 @@ CutterJson CutterCore::getSignatureInfo()
if (!signature) {
return {};
}
return parseJson(signature, nullptr);
return parseJson("signature", signature, nullptr);
}
bool CutterCore::existsFileInfo()
{
CORE_LOCK();
const RzBinInfo *info = rz_bin_get_info(core->bin);
RzBinObject *bobj = rz_bin_cur_object(core->bin);
if (!bobj) {
return false;
}
const RzBinInfo *info = rz_bin_object_get_info(bobj);
if (!(info && info->rclass)) {
return false;
}
@ -1666,7 +1641,7 @@ QVector<Chunk> CutterCore::getHeapChunks(RVA arena_addr)
rz_list_free(arenas);
return chunks_vector;
}
m_arena = ((RzArenaListItem *)rz_list_get_head_data(arenas))->addr;
m_arena = ((RzArenaListItem *)rz_list_first(arenas))->addr;
rz_list_free(arenas);
} else {
m_arena = arena_addr;
@ -3086,7 +3061,7 @@ QList<FunctionDescription> CutterCore::getAllFunctions()
function.linearSize = rz_analysis_function_linear_size(fcn);
function.nargs = rz_analysis_arg_count(fcn);
function.nlocals = rz_analysis_var_local_count(fcn);
function.nbbs = rz_list_length(fcn->bbs);
function.nbbs = rz_pvector_len(fcn->bbs);
function.calltype = fcn->cc ? QString::fromUtf8(fcn->cc) : QString();
function.name = fcn->name ? QString::fromUtf8(fcn->name) : QString();
function.edges = rz_analysis_function_count_edges(fcn, nullptr);
@ -3207,11 +3182,11 @@ QList<SymbolDescription> CutterCore::getAllSymbols()
}
}
const RzList *entries = rz_bin_object_get_entries(bf->o);
if (entries) {
const RzPVector *entries = rz_bin_object_get_entries(bf->o);
if (symbols) {
/* list entrypoints as symbols too */
int n = 0;
for (const auto &entry : CutterRzList<RzBinSymbol>(entries)) {
for (const auto &entry : CutterPVector<RzBinAddr>(entries)) {
SymbolDescription symbol;
symbol.vaddr = entry->vaddr;
symbol.name = QString("entry") + QString::number(n++);
@ -3443,13 +3418,13 @@ QList<SectionDescription> CutterCore::getAllSections()
section.size = sect->size;
section.perm = rz_str_rwx_i(sect->perm);
if (sect->size > 0) {
HtPP *digests = rz_core_bin_create_digests(core, sect->paddr, sect->size, hashnames);
HtSS *digests = rz_core_bin_create_digests(core, sect->paddr, sect->size, hashnames);
if (!digests) {
continue;
}
const char *entropy = (const char *)ht_pp_find(digests, "entropy", NULL);
const char *entropy = (const char *)ht_ss_find(digests, "entropy", NULL);
section.entropy = rz_str_get(entropy);
ht_pp_free(digests);
ht_ss_free(digests);
}
sections << section;
@ -3524,10 +3499,8 @@ QList<EntrypointDescription> CutterCore::getAllEntrypoint()
ut64 laddr = rz_bin_get_laddr(core->bin);
QList<EntrypointDescription> qList;
const RzList *entries = rz_bin_object_get_entries(bf->o);
RzListIter *iter;
RzBinAddr *entry;
CutterRzListForeach (entries, iter, RzBinAddr, entry) {
const RzPVector *entries = rz_bin_object_get_entries(bf->o);
for (const auto &entry : CutterPVector<RzBinAddr>(entries)) {
if (entry->type != RZ_BIN_ENTRY_TYPE_PROGRAM) {
continue;
}
@ -4342,6 +4315,7 @@ void CutterCore::loadScript(const QString &scriptname)
{
CORE_LOCK();
rz_core_cmd_file(core, scriptname.toUtf8().constData());
rz_cons_flush();
}
triggerRefreshAll();
}
@ -4360,10 +4334,9 @@ QString CutterCore::getVersionInformation()
const char *name;
const char *(*callback)();
} vcs[] = {
{ "rz_analysis", &rz_analysis_version },
{ "rz_arch", &rz_arch_version },
{ "rz_lib", &rz_lib_version },
{ "rz_egg", &rz_egg_version },
{ "rz_asm", &rz_asm_version },
{ "rz_bin", &rz_bin_version },
{ "rz_cons", &rz_cons_version },
{ "rz_flag", &rz_flag_version },
@ -4376,7 +4349,6 @@ QString CutterCore::getVersionInformation()
#if !USE_LIB_MAGIC
{ "rz_magic", &rz_magic_version },
#endif
{ "rz_parse", &rz_parse_version },
{ "rz_reg", &rz_reg_version },
{ "rz_sign", &rz_sign_version },
{ "rz_search", &rz_search_version },

View File

@ -62,8 +62,6 @@ struct CUTTER_EXPORT RegisterRef
QString name;
};
using PRzAnalysisBytes = std::unique_ptr<RzAnalysisBytes, decltype(rz_analysis_bytes_free) *>;
class CUTTER_EXPORT CutterCore : public QObject
{
Q_OBJECT
@ -183,13 +181,13 @@ public:
QString getRizinVersionReadable(const char *program = nullptr);
QString getVersionInformation();
CutterJson parseJson(char *res, const char *cmd = nullptr);
CutterJson parseJson(char *res, const QString &cmd = QString())
CutterJson parseJson(const char *name, char *res, const char *cmd = nullptr);
CutterJson parseJson(const char *name, char *res, const QString &cmd = QString())
{
return parseJson(res, cmd.isNull() ? nullptr : cmd.toLocal8Bit().constData());
return parseJson(name, res, cmd.isNull() ? nullptr : cmd.toLocal8Bit().constData());
}
QStringList autocomplete(const QString &cmd, RzLinePromptType promptType, size_t limit = 4096);
QStringList autocomplete(const QString &cmd, RzLinePromptType promptType);
/* Functions methods */
void renameFunction(const RVA offset, const QString &newName);
@ -248,7 +246,7 @@ public:
QString getGlobalVariableType(RVA offset);
/* Edition functions */
PRzAnalysisBytes getRzAnalysisBytesSingle(RVA addr);
CutterRzIter<RzAnalysisBytes> getRzAnalysisBytesSingle(RVA addr);
QString getInstructionBytes(RVA addr);
QString getInstructionOpcode(RVA addr);
void editInstruction(RVA addr, const QString &inst, bool fillWithNops = false);

View File

@ -164,4 +164,30 @@ public:
iterator end() const { return iterator(nullptr); }
};
template<typename T>
class CutterRzIter
{
UniquePtrC<RzIterator, &rz_iterator_free> rzIter;
public:
CutterRzIter(RzIterator *rzIter) : rzIter(rzIter)
{
// immediately attempt advancing by 1, otherwise it's hard to distinguish whether current
// element is null due to not having called next, or due to having run out of elements
if (rzIter) {
++*this;
}
}
CutterRzIter<T> &operator++()
{
rz_iterator_next(rzIter.get());
return *this;
}
operator bool() { return rzIter && rzIter->cur; }
T &operator*() { return *reinterpret_cast<RzAnalysisBytes *>(rzIter->cur); }
T *get() { return reinterpret_cast<RzAnalysisBytes *>(rzIter->cur); }
T *operator->() { return reinterpret_cast<RzAnalysisBytes *>(rzIter->cur); }
};
#endif // RIZINCPP_H

View File

@ -24,7 +24,11 @@ VersionInfoDialog::~VersionInfoDialog() {}
void VersionInfoDialog::fillVersionInfo()
{
RzCoreLocked core(Core());
const RzBinInfo *info = rz_bin_get_info(core->bin);
RzBinObject *bobj = rz_bin_cur_object(core->bin);
if (!bobj) {
return;
}
const RzBinInfo *info = rz_bin_object_get_info(bobj);
if (!info || !info->rclass) {
return;
}

View File

@ -520,7 +520,7 @@ void DisassemblyContextMenu::aboutToShowSlot()
if (ab && ab->op) {
const char *opexstr = RZ_STRBUF_SAFEGET(&ab->op->opex);
CutterJson operands = Core()->parseJson(strdup(opexstr), nullptr);
CutterJson operands = Core()->parseJson("opex", strdup(opexstr), nullptr);
// Loop through both the operands of the instruction
for (const CutterJson operand : operands) {

View File

@ -25,6 +25,11 @@
<update_contact>xarkes</update_contact>
<releases>
<release version="2.3.3" date="2024-02-24" />
<release version="2.3.2" date="2023-09-14" />
<release version="2.3.1" date="2023-08-20" />
<release version="2.3.0" date="2023-08-05" />
<release version="2.2.1" date="2023-05-15" />
<release version="2.2.0" date="2023-02-22" />
<release version="2.1.2" date="2022-09-11" />
<release version="2.1.1" date="2022-09-10" />

View File

@ -52,7 +52,8 @@ void Dashboard::updateContents()
}
// Add file hashes, analysis info and libraries
RzBinInfo *binInfo = rz_bin_get_info(core->bin);
RzBinObject *bobj = rz_bin_cur_object(core->bin);
const RzBinInfo *binInfo = bobj ? rz_bin_object_get_info(bobj) : nullptr;
setPlainText(ui->fileEdit, binInfo ? binInfo->file : "");
setPlainText(ui->formatEdit, binInfo ? binInfo->rclass : "");
@ -211,7 +212,7 @@ void Dashboard::setPlainText(QLineEdit *textBox, const QString &text)
* @brief Setting boolean values of binary information in dashboard
* @param RzBinInfo
*/
void Dashboard::setRzBinInfo(RzBinInfo *binInfo)
void Dashboard::setRzBinInfo(const RzBinInfo *binInfo)
{
setPlainText(ui->vaEdit, binInfo ? setBoolText(binInfo->has_va) : "");
setPlainText(ui->canaryEdit, binInfo ? setBoolText(binInfo->has_canary) : "");

View File

@ -33,7 +33,7 @@ private slots:
private:
std::unique_ptr<Ui::Dashboard> ui;
void setPlainText(QLineEdit *textBox, const QString &text);
void setRzBinInfo(RzBinInfo *binInfo);
void setRzBinInfo(const RzBinInfo *binInfo);
const char *setBoolText(bool value);
QWidget *hashesWidget = nullptr;

View File

@ -225,7 +225,7 @@ void DisassemblerGraphView::loadCurrentGraph()
return;
}
for (const auto &bbi : CutterRzList<RzAnalysisBlock>(fcn->bbs)) {
for (const auto &bbi : CutterPVector<RzAnalysisBlock>(fcn->bbs)) {
RVA bbiFail = bbi->fail;
RVA bbiJump = bbi->jump;

View File

@ -14,96 +14,97 @@
Basic familiarity with graph algorithms is recommended.
# Terms used:
- **Vertex**, **node**, **block** - read description of graph for definition. Within this text
vertex and node are used interchangeably with block due to code being written for visualizing basic
- **Vertex**, **node**, **block** - see the definition of graph. Within this text
vertex/node/block are used interchangeably due to the code being purposed for visualizing basic
block control flow graph.
- **edge** - read description of graph for definition for precise definition.
- **DAG** - directed acyclic graph, graph using directed edges which doesn't have cycles. DAG may
contain loops if following them would require going in both directions of edges. Example 1->2 1->3
3->2 is a DAG, 2->1 1->3 3->2 isn't a DAG.
- **edge** - see the definition of graph.
- **DAG** - directed acyclic graph, a graph using directed edges which doesn't have cycles. A DAG
may contain loops if following them would require going in both directions of edges. Example 1->2
1->3 3->2 is a DAG, 2->1 1->3 3->2 isn't a DAG.
- **DFS** - depth first search, a graph traversal algorithm
- **toposort** - topological sorting, process of ordering a DAG vertices that all edges go from
vertices earlier in the toposort order to vertices later in toposort order. There are multiple
algorithms for implementing toposort operation. Single DAG can have multiple valid topological
- **toposort** - topological sorting, the process of ordering a DAG vertices that results in all
edges going from vertices earlier in the toposort order to vertices later in toposort order. There
are multiple algorithms implementing toposort. A single DAG can have multiple valid topological
orderings, a toposort algorithm can be designed to prioritize a specific one from all valid toposort
orders. Example: for graph 1->4, 2->1, 2->3, 3->4 valid topological orders are [2,1,3,4] and
[2,3,1,4].
# High level structure of the algorithm
1. select subset of edges that form a DAG (remove cycles)
2. toposort the DAG
3. choose a subset of edges that form a tree and assign layers
4. assign node positions within grid using tree structure, child subtrees are placed side by side
# High level algorithm structure
1. Select a subset of edges that form a DAG (remove cycles)
2. Toposort the DAG
3. Choose a subset of edges that form a tree and assign layers
4. Assign node positions within grid using tree structure, child subtrees are placed side by side
with parent on top
5. perform edge routing
6. calculate column and row pixel positions based on node sizes and amount edges between the rows
7. [optional] layout compacting
5. Perform edge routing
6. Calculate column and row pixel positions based on node sizes and amount edges between the rows
7. [optional] Layout compacting
Contrary to many other layered graph drawing algorithm this implementation doesn't perform node
reordering to minimize edge crossing. This simplifies implementation, and preserves original control
flow structure for conditional jumps ( true jump on one side, false jump on other). Due to most of
control flow being result of structured programming constructs like if/then/else and loops,
resulting layout is usually readable without node reordering within layers.
Contrary to many other layered graph-drawing algorithms this implementation doesn't perform node
reordering to minimize edge crossing. This simplifies the implementation, and preserves the original
control-flow structure for conditional jumps ( true jump on one side, false jump on other). Due to
most of the control flow resulting from structured programming constructs like if/then/else and
loops, the resulting layout is usually readable without node reordering within layers.
# Description of grid.
To simplify the layout algorithm initial steps assume that all nodes have the same size and edges
are zero width. After placing the nodes and routing the edges it is known which nodes are in in
which row and column, how many edges are between each pair of rows. Using this information positions
are converted from the grid cells to pixel coordinates. Routing 0 width edges between rows can also
# Grid
To simplify the layout algorithm, its initial steps assume that all nodes have the same size and
that edges are zero-width. After nodes placement and edges rounting, the row/column of nodes is
known as well as the amount of edges between each pair of rows. Using this information, positions
are converted from grid cells to pixel coordinates. Routing zero-width edges between rows can also
be interpreted as every second row and column being reserved for edges. The row numbers in code are
using first interpretation. To allow better centering of nodes one above other each node is 2
using the first interpretation. To allow better centering of nodes one above other, each node is 2
columns wide and 1 row high.
\image html graph_grid.svg
# 1-2 Cycle removal and toposort
Cycle removal and toposort are done at the same time during single DFS traversal. In case entrypoint
is part of a loop DFS started from entrypoint. This ensures that entrypoint is at the top of
resulting layout if possible. Resulting toposort order is used in many of the following layout steps
that require calculating some property of a vertex based on child property or the other way around.
Using toposort order such operations can be implemented iteration through array in either forward or
reverse direction. To prevent running out of stack memory when processing large graphs DFS is
implemented non-recursively.
Cycle removal and toposort are done in a single DFS traversal. In case the entrypoint
is part of a loop, the DFS starts from the entrypoint. This ensures that the entrypoint is at the
top of resulting layout, if possible. The resulting toposort order is used in many of the following
layout steps that require calculating some property of a vertex based on a child property or the
other way around. Using toposort order, such operations can be implemented by array iteration in
either forward/backward direction. To prevent running out of stack memory when processing large
graphs, DFS is implemented non-recursively.
# Row assignment
Rows are assigned in toposort order from top to bottom, with nodes row being max(predecessor.row)+1.
This ensures that loop edges are only ones going from deeper levels to previous layers.
This ensures that loop back-edges are the only edges going from lower to higher layers.
To further simply node placement a subset of edges is selected which forms a tree. This turns DAG
drawing problem into a tree drawing problem. For each node in level n following nodes which have
level exactly n+1 are greedily assigned as child nodes in tree. If a node already has parent
assigned then corresponding edge is not part of tree.
To further simply node placement, a subset of edges is selected which forms a tree. This turns a DAG
drawing problem into a tree drawing problem. For each node in level n the following nodes with
level exactly n+1 are greedily assigned as child nodes in the tree. If a node already has a parent
assigned then the corresponding edge is not part of the tree.
# Node position assignment
# Node placement
Since the graph has been reduced to a tree, node placement is more or less putting subtrees side by
side with parent on top. There is some room for interpretation what exactly side by side means and
where exactly on top is. Drawing the graph either too dense or too big may make it less readable so
there are configuration options which allow choosing these things resulting in more or less dense
layout.
side with parent on top. There is some room for interpretation as to what exactly 'side by side'
means and where exactly 'on top' is: drawing the graph either too dense or too sparse may make it
less readable, so there are configuration options which allow choosing these things resulting in
more or less dense layout.
Once the subtrees are placed side by side. Parent node can be placed either in the middle of
horizontal bounds or in the middle of direct children. First option results in narrower layout and
more vertical columns. Second option results in nodes being more spread out which may help seeing
where each edge goes.
Once the subtrees are placed side by side, the parent node can be placed either in the middle of
the horizontal bounds or in the middle of its direct children. The first option results in narrower
layout and more vertical columns, while the second option results in more spread out layout which
may help seeing where each edge goes.
In more compact mode two subtrees are placed side by side taking into account their shape. In wider
mode bounding box of shorter subtree is used instead of exact shape. This gives slightly sparse
layout without it being too wide.
In compact mode two subtrees are placed side by side accounting for their shape. In wider
mode the bounding box of the shorter subtree is used instead of its exact shape. This gives slightly
sparser layout without being too wide.
\image html graph_parent_placement.svg
# Edge routing
Edge routing can be split into: main column selection, rough routing, segment offset calculation.
Edge routing can be split into: main column selection, rough routing, and segment offset
calculation.
Transition from source to target row is done using single vertical segment. This is called main
column.
Transition from source to target row is done using a single vertical segment. This segment is called
the 'main column'.
A sweep line is used for computing main columns: Blocks and edges are processed as events top to
Main columns are computed using a sweep line: blocks and edges are processed as events top to
bottom based off their row (max(start row, end row) for edges). Blocked columns are tracked in a
tree structure which allows searching nearest column with at least last N rows empty. The column
of the starting block is favored for the main column, otherwise the target block's column is chosen
@ -114,10 +115,9 @@ true or false branch. In case of upward edges it is allowed to choose a column o
is slightly further than nearest empty to reduce the chance of producing tilted figure 8 shaped
crossing between two blocks.
Rough routing creates the path of edge using up to 5 segments using grid coordinates.
Due to nodes being placed in a grid. Horizontal segments of edges can't intersect with any nodes.
Due to nodes being placed in a grid, horizontal segments of edges can't intersect with any nodes.
The path for edges is chosen so that it consists of at most 5 segments, typically resulting in
sideways U shape or square Z shape.
sideways U shape or square Z shape:
- short vertical segment from node to horizontal line
- move to empty column
- vertical segment between starting row and end row
@ -134,45 +134,49 @@ ensures that two segments don't overlap. Segment offsets within each column are
with some heuristics for assignment order to reduce amount of edge crossings and result in more
visually pleasing output for a typical CFG graph. Each segment gets assigned an offset that is
maximum of previously assigned offsets overlapping with current segment + segment spacing.
Assignment order is chosen based on:
* direction of previous and last segment - helps reducing crossings and place the segments between
Assignment order is based on:
- direction of previous and last segment - helps reducing crossings and place the segments between
nodes
* segment length - reduces crossing when segment endpoints have the same structure as valid
- segment length - reduces crossing when segment endpoints have the same structure as valid
parentheses expression
* edge length - establishes some kind of order when single node is connected to many edges,
- edge length - establishes some kind of order when single node is connected to many edges,
typically a block with switch statement or block after switch statement.
# Layout compacting
Doing the layout within a grid causes minimal spacing to be limited by widest and tallest block
within each column and row. One common case is block with function entrypoint being wider due to
function name causing wide horizontal space between branching blocks. Another case is rows in two
Doing the layout on a grid limits the minimal spacing to the widest block within a column and
tallest block within a row. One common case is a function-entry block being wider due to the
function name, causing wide horizontal space between branching blocks. Another case is rows in two
parallel columns being aligned.
\image html layout_compacting.svg
Both problems are mitigated by squishing graph. Compressing in each of the two direction is done
Both problems are mitigated by squishing the graph. Compressing in each of the two direction is done
separately. The process is defined as liner program. Each variable represents a position of edge
segment or node in the direction being optimized.
Following constraints are used
The following constraints are used:
- Keep the order with nearest segments.
- If the node has two outgoing edges, one to the node on left side and other to the right, keep them
on the corresponding side of node's center.
- For all edges keep the node which is above above. This helps when vertical block spacing is set
bigger than double edge spacing and edge shadows relationship between two blocks.
- If a node has two outgoing edges, one to the left and one to the right, keep them
on the corresponding side of the node's center.
- Equality constraint to keep relative position between nodes and and segments directly connected to
them.
- Equality constraint to keep the node centered when control flow merges
In the vertical direction objective function minimizes y positions of nodes and lengths of vertical
segments. In the horizontal direction objective function minimizes lengths of horizontal segments.
- For all blocks connected by forward edge, keep the vertical distance at least as big as configured
block vertical spacing. This helps when vertical block-spacing is set bigger than double edge
spacing and an edge shadows relationship between two blocks.
- Equality constraint to keep a node centered when control flow merges.
In the resulting linear program all constraints beside x_i >= 0 consist of exactly two variables:
In the vertical direction the objective function minimizes y positions of nodes and lengths of
vertical segments. In the horizontal direction the objective function minimizes the lengths of
horizontal segments.
In the resulting linear program all constraints besides x_i >= 0 consist of exactly two variables:
either x_i - x_j <= c_k or x_i = x_j + c_k.
Since it isn't necessary get perfect solution and to avoid worst case performance current
implementation isn't using a general purpose linear programming solver. Each variable is changed
until constraint is reached and afterwards variables are grouped and changed together.
Since a perfect solution isn't necessary and to avoid worst case performance, the current
implementation isn't using a general purpose linear solver. Instead, each variable is modified
until a constraint is satisfied and afterwards variables are grouped and modified together.
*/

View File

@ -94,7 +94,7 @@ void GraphvizLayout::CalculateLayout(std::unordered_map<ut64, GraphBlock> &block
std::unordered_map<ut64, Agnode_t *> nodes;
for (const auto &block : blocks) {
nodes[block.first] = agnode(g, nullptr, TRUE);
nodes[block.first] = agnode(g, nullptr, true);
}
std::vector<std::string> strc;
@ -143,7 +143,7 @@ void GraphvizLayout::CalculateLayout(std::unordered_map<ut64, GraphBlock> &block
if (v == nodes.end()) {
continue;
}
auto e = agedge(g, u, v->second, nullptr, TRUE);
auto e = agedge(g, u, v->second, nullptr, true);
edges[{ blockIt.first, edge.target }] = e;
if (loopEdges.find({ blockIt.first, edge.target }) != loopEdges.end()) {
agxset(e, constraintAttr, STR("0"));
@ -193,11 +193,11 @@ void GraphvizLayout::CalculateLayout(std::unordered_map<ut64, GraphBlock> &block
if (it != edges.end()) {
auto e = it->second;
if (auto spl = ED_spl(e)) {
for (int i = 0; i < 1 && i < spl->size; i++) {
for (size_t i = 0; i < 1 && i < spl->size; i++) {
auto bz = spl->list[i];
edge.polyline.clear();
edge.polyline.reserve(bz.size + 1);
for (int j = 0; j < bz.size; j++) {
for (size_t j = 0; j < bz.size; j++) {
edge.polyline.push_back(QPointF(bz.list[j].x, bz.list[j].y));
}
QPointF last(0, 0);

View File

@ -9,13 +9,6 @@
#define DEBUGGED_PID (-1)
enum ColumnIndex {
COLUMN_PID = 0,
COLUMN_UID,
COLUMN_STATUS,
COLUMN_PATH,
};
ProcessesWidget::ProcessesWidget(MainWindow *main)
: CutterDockWidget(main), ui(new Ui::ProcessesWidget)
{
@ -23,10 +16,14 @@ ProcessesWidget::ProcessesWidget(MainWindow *main)
// Setup processes model
modelProcesses = new QStandardItemModel(1, 4, this);
modelProcesses->setHorizontalHeaderItem(COLUMN_PID, new QStandardItem(tr("PID")));
modelProcesses->setHorizontalHeaderItem(COLUMN_UID, new QStandardItem(tr("UID")));
modelProcesses->setHorizontalHeaderItem(COLUMN_STATUS, new QStandardItem(tr("Status")));
modelProcesses->setHorizontalHeaderItem(COLUMN_PATH, new QStandardItem(tr("Path")));
modelProcesses->setHorizontalHeaderItem(ProcessesWidget::COLUMN_PID,
new QStandardItem(tr("PID")));
modelProcesses->setHorizontalHeaderItem(ProcessesWidget::COLUMN_UID,
new QStandardItem(tr("UID")));
modelProcesses->setHorizontalHeaderItem(ProcessesWidget::COLUMN_STATUS,
new QStandardItem(tr("Status")));
modelProcesses->setHorizontalHeaderItem(ProcessesWidget::COLUMN_PATH,
new QStandardItem(tr("Path")));
ui->viewProcesses->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
ui->viewProcesses->verticalHeader()->setVisible(false);
ui->viewProcesses->setFont(Config()->getFont());
@ -129,10 +126,10 @@ void ProcessesWidget::setProcessesGrid()
rowStatus->setFont(font);
rowPath->setFont(font);
modelProcesses->setItem(i, COLUMN_PID, rowPid);
modelProcesses->setItem(i, COLUMN_UID, rowUid);
modelProcesses->setItem(i, COLUMN_STATUS, rowStatus);
modelProcesses->setItem(i, COLUMN_PATH, rowPath);
modelProcesses->setItem(i, ProcessesWidget::COLUMN_PID, rowPid);
modelProcesses->setItem(i, ProcessesWidget::COLUMN_UID, rowUid);
modelProcesses->setItem(i, ProcessesWidget::COLUMN_STATUS, rowStatus);
modelProcesses->setItem(i, ProcessesWidget::COLUMN_PATH, rowPath);
i++;
}
@ -155,7 +152,7 @@ void ProcessesWidget::onActivated(const QModelIndex &index)
if (!index.isValid())
return;
int pid = modelFilter->data(index.sibling(index.row(), COLUMN_PID)).toInt();
int pid = modelFilter->data(index.sibling(index.row(), ProcessesWidget::COLUMN_PID)).toInt();
// Verify that the selected pid is still in the processes list since dp= will
// attach to any given id. If it isn't found simply update the UI.
for (const auto &value : Core()->getAllProcesses()) {
@ -185,7 +182,7 @@ ProcessesFilterModel::ProcessesFilterModel(QObject *parent) : QSortFilterProxyMo
bool ProcessesFilterModel::filterAcceptsRow(int row, const QModelIndex &parent) const
{
// All columns are checked for a match
for (int i = COLUMN_PID; i <= COLUMN_PATH; ++i) {
for (int i = ProcessesWidget::COLUMN_PID; i <= ProcessesWidget::COLUMN_PATH; ++i) {
QModelIndex index = sourceModel()->index(row, i, parent);
if (qhelpers::filterStringContains(sourceModel()->data(index).toString(), this)) {
return true;

View File

@ -31,6 +31,13 @@ class ProcessesWidget : public CutterDockWidget
Q_OBJECT
public:
enum ColumnIndex {
COLUMN_PID = 0,
COLUMN_UID,
COLUMN_STATUS,
COLUMN_PATH,
};
explicit ProcessesWidget(MainWindow *main);
~ProcessesWidget();

View File

@ -9,21 +9,17 @@
#define DEBUGGED_PID (-1)
enum ColumnIndex {
COLUMN_PID = 0,
COLUMN_STATUS,
COLUMN_PATH,
};
ThreadsWidget::ThreadsWidget(MainWindow *main) : CutterDockWidget(main), ui(new Ui::ThreadsWidget)
{
ui->setupUi(this);
// Setup threads model
modelThreads = new QStandardItemModel(1, 3, this);
modelThreads->setHorizontalHeaderItem(COLUMN_PID, new QStandardItem(tr("PID")));
modelThreads->setHorizontalHeaderItem(COLUMN_STATUS, new QStandardItem(tr("Status")));
modelThreads->setHorizontalHeaderItem(COLUMN_PATH, new QStandardItem(tr("Path")));
modelThreads->setHorizontalHeaderItem(ThreadsWidget::COLUMN_PID, new QStandardItem(tr("PID")));
modelThreads->setHorizontalHeaderItem(ThreadsWidget::COLUMN_STATUS,
new QStandardItem(tr("Status")));
modelThreads->setHorizontalHeaderItem(ThreadsWidget::COLUMN_PATH,
new QStandardItem(tr("Path")));
ui->viewThreads->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
ui->viewThreads->verticalHeader()->setVisible(false);
ui->viewThreads->setFont(Config()->getFont());
@ -120,9 +116,9 @@ void ThreadsWidget::setThreadsGrid()
rowStatus->setFont(font);
QStandardItem *rowPath = new QStandardItem(path);
rowPath->setFont(font);
modelThreads->setItem(i, COLUMN_PID, rowPid);
modelThreads->setItem(i, COLUMN_STATUS, rowStatus);
modelThreads->setItem(i, COLUMN_PATH, rowPath);
modelThreads->setItem(i, ThreadsWidget::COLUMN_PID, rowPid);
modelThreads->setItem(i, ThreadsWidget::COLUMN_STATUS, rowStatus);
modelThreads->setItem(i, ThreadsWidget::COLUMN_PATH, rowPath);
i++;
}
@ -146,7 +142,7 @@ void ThreadsWidget::onActivated(const QModelIndex &index)
if (!index.isValid())
return;
int tid = modelFilter->data(index.sibling(index.row(), COLUMN_PID)).toInt();
int tid = modelFilter->data(index.sibling(index.row(), ThreadsWidget::COLUMN_PID)).toInt();
// Verify that the selected tid is still in the threads list since dpt= will
// attach to any given id. If it isn't found simply update the UI.
@ -169,7 +165,7 @@ ThreadsFilterModel::ThreadsFilterModel(QObject *parent) : QSortFilterProxyModel(
bool ThreadsFilterModel::filterAcceptsRow(int row, const QModelIndex &parent) const
{
// All columns are checked for a match
for (int i = COLUMN_PID; i <= COLUMN_PATH; ++i) {
for (int i = ThreadsWidget::COLUMN_PID; i <= ThreadsWidget::COLUMN_PATH; ++i) {
QModelIndex index = sourceModel()->index(row, i, parent);
if (qhelpers::filterStringContains(sourceModel()->data(index).toString(), this)) {
return true;

View File

@ -31,6 +31,12 @@ class ThreadsWidget : public CutterDockWidget
Q_OBJECT
public:
enum ColumnIndex {
COLUMN_PID = 0,
COLUMN_STATUS,
COLUMN_PATH,
};
explicit ThreadsWidget(MainWindow *main);
~ThreadsWidget();