new CommandLine class

implemented crashdump as a command, fixed dump_running_process.bat to use it
attach to console if present instead of always create one
This commit is contained in:
isanae
2020-11-03 11:39:01 -05:00
parent b103f29775
commit 5e528fb4cf
8 changed files with 318 additions and 30 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
pushd "%~dp0"
start ModOrganizer.exe --crashdump
start ModOrganizer.exe crashdump
+1
View File
@@ -11,6 +11,7 @@ endif()
add_filter(NAME src/application GROUPS
iuserinterface
commandline
main
moapplication
moshortcut
+205
View File
@@ -0,0 +1,205 @@
#include "commandline.h"
#include "env.h"
namespace cl
{
CommandLine::CommandLine()
{
createOptions();
m_commands.push_back(std::make_unique<CrashDumpCommand>());
}
int CommandLine::run(int argc, char** argv)
{
try
{
po::variables_map vm;
auto parsed = po::command_line_parser(argc, argv)
.options(m_allOptions)
.positional(m_positional)
.allow_unregistered()
.run();
po::store(parsed, vm);
if (vm.count("command")) {
const auto cmd = vm["command"].as<std::string>();
for (auto&& c : m_commands) {
if (c->name() == cmd) {
auto co = c->options();
co.add_options()
("help", "shows this message");
auto opts = po::collect_unrecognized(
parsed.options, po::include_positional);
// remove the command name itself
opts.erase(opts.begin());
parsed = po::command_line_parser(opts)
.options(co)
.run();
po::store(parsed, vm);
if (vm.count("help")) {
env::Console console;
std::cout << usage(c.get()) << "\n";
return 0;
}
return c->run(vm);
}
}
}
if (vm.count("help")) {
env::Console console;
std::cout << usage() << "\n";
return 0;
}
return -1;
}
catch(po::error& e)
{
env::Console console;
std::cerr << e.what() << "\n";
std::cerr << usage() << "\n";
return 1;
}
}
void CommandLine::createOptions()
{
m_visibleOptions.add_options()
("help", "shows this message");
po::options_description options;
options.add_options()
("command", po::value<std::string>(), "command to execute");
m_positional
.add("command", 1)
.add("subargs", -1);
m_allOptions.add(m_visibleOptions);
m_allOptions.add(options);
}
std::string CommandLine::usage(const Command* c) const
{
std::ostringstream oss;
oss
<< "\n"
<< "Usage:\n";
if (c) {
oss
<< " ModOrganizer.exe [options] " << c->name() << " [command-options]\n"
<< "\n"
<< "Command options:\n"
<< c->options() << "\n";
} else {
oss
<< " ModOrganizer.exe [options] [[command] [command-options]]\n"
<< "\n"
<< "Commands:\n";
for (auto&& c : m_commands) {
oss << " " << c->name() << " " << c->description() << "\n";
}
oss << "\n";
}
oss
<< "Global options:\n"
<< m_visibleOptions << "\n";
return oss.str();
}
std::string Command::name() const
{
return meta().name;
}
std::string Command::description() const
{
return meta().description;
}
po::options_description Command::options() const
{
return doOptions();
}
po::options_description Command::doOptions() const
{
// no-op
return {};
}
std::string Command::usage() const
{
std::ostringstream oss;
oss
<< "\n"
<< "Usage:\n"
<< " ModOrganizer.exe [options] [[command] [command-options]]\n"
<< "\n"
<< "Options:\n"
<< options() << "\n";
return oss.str();
}
int Command::run(po::variables_map& vm)
{
return doRun(vm);
}
po::options_description CrashDumpCommand::doOptions() const
{
po::options_description d;
d.add_options()
("type", po::value<std::string>()->default_value("mini"), "mini|data|full");
return d;
}
Command::Meta CrashDumpCommand::meta() const
{
return {"crashdump", "writes a crashdump for a running process of MO"};
}
int CrashDumpCommand::doRun(po::variables_map& vm)
{
env::Console console;
const auto typeString = vm["type"].as<std::string>();
const auto type = env::coreDumpTypeFromString(typeString);
// dump
const auto b = env::coredumpOther(type);
if (!b) {
std::wcerr << L"\n>>>> a minidump file was not written\n\n";
}
std::wcerr << L"Press enter to continue...";
std::wcin.get();
return (b ? 0 : 1);
}
} // namespace
+62
View File
@@ -0,0 +1,62 @@
#pragma once
#include <vector>
#include <memory>
namespace cl
{
namespace po = boost::program_options;
class Command
{
public:
virtual ~Command() = default;
std::string name() const;
std::string description() const;
po::options_description options() const;
std::string usage() const;
int run(po::variables_map& vm);
protected:
struct Meta
{
std::string name, description;
};
virtual po::options_description doOptions() const;
virtual Meta meta() const = 0;
virtual int doRun(po::variables_map& vm) = 0;
};
class CrashDumpCommand : public Command
{
protected:
po::options_description doOptions() const;
Meta meta() const override;
int doRun(po::variables_map& vm) override;
};
class CommandLine
{
public:
CommandLine();
int run(int argc, char** argv);
std::string usage(const Command* c=nullptr) const;
private:
po::options_description m_visibleOptions, m_allOptions;
po::positional_options_description m_positional;
std::vector<std::unique_ptr<Command>> m_commands;
void createOptions();
};
} // namespace
+39 -3
View File
@@ -16,9 +16,15 @@ using namespace MOBase;
Console::Console()
: m_hasConsole(false), m_in(nullptr), m_out(nullptr), m_err(nullptr)
{
// open a console
if (!AllocConsole()) {
// failed, ignore
// try to attach to parent
if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
if (GetLastError() != ERROR_ACCESS_DENIED) {
// parent has no console, create one
if (!AllocConsole()) {
// failed, ignore
return;
}
}
}
m_hasConsole = true;
@@ -1032,6 +1038,36 @@ HandlePtr dumpFile()
return {};
}
CoreDumpTypes coreDumpTypeFromString(const std::string& s)
{
if (s == "data")
return env::CoreDumpTypes::Data;
else if (s == "full")
return env::CoreDumpTypes::Full;
else
return env::CoreDumpTypes::Mini;
}
std::string toString(CoreDumpTypes type)
{
switch (type)
{
case CoreDumpTypes::Mini:
return "mini";
case CoreDumpTypes::Data:
return "data";
case CoreDumpTypes::Full:
return "full";
default:
return "?";
}
}
bool createMiniDump(HANDLE process, CoreDumpTypes type)
{
const DWORD pid = GetProcessId(process);
+4
View File
@@ -307,6 +307,10 @@ enum class CoreDumpTypes
Full
};
CoreDumpTypes coreDumpTypeFromString(const std::string& s);
std::string toString(CoreDumpTypes type);
// creates a minidump file for this process
//
bool coredump(CoreDumpTypes type);
+5 -26
View File
@@ -49,6 +49,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "env.h"
#include "envmodule.h"
#include "shared/util.h"
#include "commandline.h"
#include <eh.h>
#include "shared/windows_error.h"
@@ -811,22 +812,6 @@ int runApplication(
return 1;
}
int doCoreDump(env::CoreDumpTypes type)
{
env::Console c;
// dump
const auto b = env::coredumpOther(type);
if (!b) {
std::wcerr << L"\n>>>> a minidump file was not written\n\n";
}
std::wcerr << L"Press enter to continue...";
std::wcin.get();
return (b ? 0 : 1);
}
log::Levels convertQtLevel(QtMsgType t)
{
switch (t)
@@ -895,17 +880,11 @@ void initLogging()
int main(int argc, char *argv[])
{
TimeThis tt("main to runApplication()");
cl::CommandLine cl;
// handle --crashdump first
for (int i=1; i<argc; ++i) {
if (std::strcmp(argv[i], "--crashdump") == 0) {
return doCoreDump(env::CoreDumpTypes::Mini);
} else if (std::strcmp(argv[i], "--crashdump-data") == 0) {
return doCoreDump(env::CoreDumpTypes::Data);
} else if (std::strcmp(argv[i], "--crashdump-full") == 0) {
return doCoreDump(env::CoreDumpTypes::Full);
}
}
const auto r = cl.run(argc, argv);
if (r >= 0)
return r;
initLogging();
+1
View File
@@ -64,6 +64,7 @@
#include <boost/thread.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/program_options.hpp>
// openssl
#include <tlhelp32.h>