<commit_before>// Copyright (c) 2009 Chris Pickel <sfiera@gmail.com>
//
// This file is part of rezin, a free software project.  You can redistribute it and/or modify it
// under the terms of the MIT License.

#include <fcntl.h>
#include <getopt.h>
#include <exception>
#include <map>
#include <vector>

#include "rezin/rezin.hpp"
#include "rgos/rgos.hpp"
#include "sfz/sfz.hpp"
#include "zipxx/zipxx.hpp"

using rgos::Json;
using sfz::Bytes;
using sfz::BytesPiece;
using sfz::Exception;
using sfz::MappedFile;
using sfz::Rune;
using sfz::String;
using sfz::StringPiece;
using sfz::format;
using sfz::path::basename;
using sfz::quote;
using sfz::range;
using sfz::scoped_ptr;
using sfz::string_to_int;
using std::map;
using std::vector;
using zipxx::ZipArchive;
using zipxx::ZipFileReader;

namespace io = sfz::io;
namespace macroman = sfz::macroman;
namespace utf8 = sfz::utf8;

namespace rezin {
namespace {

class Command {
  public:
    virtual ~Command() { }
    virtual void run(const ResourceFork& rsrc) = 0;
};

class LsCommand : public Command {
  public:
    LsCommand(const vector<StringPiece>& args)
        : _argc(args.size()) {
        if (args.size() > 3) {
            throw Exception(format("too many arguments to command \"ls\"."));
        }
        if (args.size() > 2) {
            if (!string_to_int(args[2], &_id)) {
                throw Exception(format("invalid resource ID {0}.", quote(args[2])));
            }
        }
        if (args.size() > 1) {
            _code.assign(args[1]);
        }
    }

    virtual void run(const ResourceFork& rsrc) {
        if (_argc == 1) {
            foreach (it, rsrc) {
                print(io::out, format("{0}\n", it->code()));
            }
        } else if (_argc == 2) {
            const ResourceType& type = rsrc.at(_code);
            foreach (it, type) {
                print(io::out, format("{0}\t{1}\n", it->id(), it->name()));
            }
        } else if (_argc == 3) {
            const ResourceEntry& entry = rsrc.at(_code).at(_id);
            print(io::out, format("{0}\t{1}\n", entry.id(), entry.name()));
        }
    }

  private:
    int _argc;
    String _code;
    int16_t _id;
};

class CatCommand : public Command {
  public:
    CatCommand(const vector<StringPiece>& args) {
        if (args.size() != 3) {
            throw Exception(format("wrong number of arguments to command \"cat\"."));
        }
        _code.assign(args[1]);
        if (!string_to_int(args[2], &_id)) {
            throw Exception(format("invalid resource ID {0}.", quote(args[2])));
        }
    }

    virtual void run(const ResourceFork& rsrc) {
        // TODO(sfiera): fix potentially surprising results of integer truncation here.
        const ResourceEntry& entry = rsrc.at(_code).at(_id);
        BytesPiece data = entry.data();
        write(1, data.data(), data.size());
    }

  private:
    String _code;
    int16_t _id;
};

class ConvertCommand : public Command {
  public:
    ConvertCommand(const vector<StringPiece>& args) {
        if (args.size() != 3) {
            throw Exception(format("wrong number of arguments to command \"convert\"."));
        }
        _code.assign(args[1]);
        if (!string_to_int(args[2], &_id)) {
            throw Exception(format("invalid resource ID {0}.", quote(args[2])));
        }
    }

    virtual void run(const ResourceFork& rsrc) {
        // TODO(sfiera): fix potentially surprising results of integer truncation here.
        const ResourceEntry& entry = rsrc.at(_code).at(_id);
        BytesPiece data = entry.data();
        Bytes converted;

        if (_code == "snd ") {
            Json snd = read_snd(data);
            write_aiff(snd, &converted);
        } else if (_code == "TEXT") {
            String string(macroman::decode(data));
            cr2nl(&string);
            converted.assign(utf8::encode(string));
        } else if (_code == "STR#") {
            Json list = read_string_list(data);
            String decoded_string;
            print_to(&decoded_string, pretty_print(list));
            converted.assign(utf8::encode(decoded_string));
        } else if (_code == "clut") {
            Json list = read_clut(data);
            String decoded_string;
            print_to(&decoded_string, pretty_print(list));
            converted.assign(utf8::encode(decoded_string));
        } else {
            print(io::err, format("warning: printing unknown resource type {0} as raw data.\n",
                        quote(_code)));
            converted.assign(data.data(), data.size());
        }
        write(1, converted.data(), converted.size());
    }

  private:
    String _code;
    int16_t _id;
};

class Source {
  public:
    virtual ~Source() { }
    virtual BytesPiece load() = 0;
};

class ResourceForkSource : public Source {
  public:
    ResourceForkSource(const StringPiece& arg)
        : _path(arg) { }

    virtual BytesPiece load() {
        _file.reset(new MappedFile(_path));
        return _file->data();
    }

  private:
    const String _path;
    scoped_ptr<MappedFile> _file;
};

class AppleSingleSource : public Source {
  public:
    AppleSingleSource(const StringPiece& arg)
        : _path(arg) { }

    virtual BytesPiece load() {
        _file.reset(new MappedFile(_path));
        _apple_single.reset(new AppleSingle(_file->data()));
        return _apple_single->at(AppleSingle::RESOURCE_FORK);
    }

  private:
    const String _path;
    scoped_ptr<MappedFile> _file;
    scoped_ptr<AppleSingle> _apple_single;
};

class ZipSource : public Source {
  public:
    ZipSource(const StringPiece& arg) {
        String arg_string(arg);
        size_t comma = arg_string.find(',');
        if (comma == String::npos) {
            throw Exception(format("invalid format to --zip-file {0}.", quote(arg_string)));
        }
        _archive_path.assign(StringPiece(arg_string).substr(0, comma));

        _file_path.append("__MACOSX/");
        _file_path.append(StringPiece(arg_string).substr(comma + 1));
        size_t loc = _file_path.rfind('/');
        _file_path.replace(loc, 1, "/._");
    }

    virtual BytesPiece load() {
        _archive.reset(new ZipArchive(_archive_path, 0));
        _file.reset(new ZipFileReader(_archive.get(), _file_path));
        _apple_double.reset(new AppleDouble(_file->data()));
        return _apple_double->at(AppleDouble::RESOURCE_FORK);
    }

  private:
    String _archive_path;
    String _file_path;
    scoped_ptr<ZipArchive> _archive;
    scoped_ptr<ZipFileReader> _file;
    scoped_ptr<AppleDouble> _apple_double;
};

int main(int argc, char** argv) {
    const String binary_name(utf8::decode(argv[0]));
    scoped_ptr<Command> command;
    scoped_ptr<Source> source;

    try {
        opterr = 0;
        const option longopts[] = {
            { "apple-single",   required_argument,  NULL,   'a' },
            { "resource-fork",  required_argument,  NULL,   'r' },
            { "zip-file",       required_argument,  NULL,   'z' },
            { NULL,             0,                  NULL,   0 }
        };

        while (true) {
            const char ch = getopt_long(argc, argv, "a:r:z:", longopts, NULL);
            if (ch == -1) {
                break;
            }

            String arg;
            if (optarg) {
                arg.assign(utf8::decode(optarg));
            }
            switch (ch) {
              case 'a':
                if (source.get() != NULL) {
                    throw Exception(format("more than one source specified."));
                }
                source.reset(new AppleSingleSource(arg));
                break;

              case 'r':
                if (source.get() != NULL) {
                    throw Exception(format("more than one source specified."));
                }
                source.reset(new ResourceForkSource(arg));
                break;

              case 'z':
                if (source.get() != NULL) {
                    throw Exception(format("more than one source specified."));
                }
                source.reset(new ZipSource(arg));
                break;

              default:
                {
                    String opt;
                    if (optopt == '\0') {
                        opt.assign(utf8::decode(argv[optind - 1]));
                    } else {
                        opt.assign(format("-{0}", char(optopt)));
                    }
                    throw Exception(format("unrecognized option {0}.", quote(opt)));
                }
                break;
            }
        }

        argc -= optind;
        argv += optind;
        if (argc < 1) {
            throw Exception(format("missing command."));
        }
        if (source.get() == NULL) {
            throw Exception(format("must provide a source."));
        }

        String storage;
        vector<StringPiece> args;
        for (int i = 0; i < argc; ++i) {
            String arg(utf8::decode(argv[i]));
            storage.append(arg);
            args.push_back(StringPiece(storage).substr(storage.size() - arg.size()));
        }

        if (args[0] == "ls") {
            command.reset(new LsCommand(args));
        } else if (args[0] == "cat") {
            command.reset(new CatCommand(args));
        } else if (args[0] == "convert") {
            command.reset(new ConvertCommand(args));
        } else {
            throw Exception(format("unknown command '{0}'.", args[0]));
        }
    } catch (Exception& e) {
        print(io::err, format(
                    "{0}: {1}\n"
                    "usage: {0} [OPTIONS] ls [TYPE [ID]]\n"
                    "       {0} [OPTIONS] cat TYPE ID\n"
                    "       {0} [OPTIONS] convert TYPE ID\n"
                    "options:\n"
                    "    -a|--apple-single=FILE\n"
                    "    -r|--resource-fork=FILE\n"
                    "    -z|--zip-file=ZIP,FILE\n",
                    basename(binary_name), e.message()));
        return 1;
    }

    try {
        ResourceFork rsrc(source->load());
        command->run(rsrc);
    } catch (Exception& e) {
        print(io::err, format("{0}: {1}\n", binary_name, e.message()));
        return 1;
    }

    return 0;
}

}  // namespace
}  // namespace rezin

int main(int argc, char** argv) {
    return rezin::main(argc, argv);
}
<commit_msg>Remove resolved TODO about integer truncation.<commit_after>// Copyright (c) 2009 Chris Pickel <sfiera@gmail.com>
//
// This file is part of rezin, a free software project.  You can redistribute it and/or modify it
// under the terms of the MIT License.

#include <fcntl.h>
#include <getopt.h>
#include <exception>
#include <map>
#include <vector>

#include "rezin/rezin.hpp"
#include "rgos/rgos.hpp"
#include "sfz/sfz.hpp"
#include "zipxx/zipxx.hpp"

using rgos::Json;
using sfz::Bytes;
using sfz::BytesPiece;
using sfz::Exception;
using sfz::MappedFile;
using sfz::Rune;
using sfz::String;
using sfz::StringPiece;
using sfz::format;
using sfz::path::basename;
using sfz::quote;
using sfz::range;
using sfz::scoped_ptr;
using sfz::string_to_int;
using std::map;
using std::vector;
using zipxx::ZipArchive;
using zipxx::ZipFileReader;

namespace io = sfz::io;
namespace macroman = sfz::macroman;
namespace utf8 = sfz::utf8;

namespace rezin {
namespace {

class Command {
  public:
    virtual ~Command() { }
    virtual void run(const ResourceFork& rsrc) = 0;
};

class LsCommand : public Command {
  public:
    LsCommand(const vector<StringPiece>& args)
        : _argc(args.size()) {
        if (args.size() > 3) {
            throw Exception(format("too many arguments to command \"ls\"."));
        }
        if (args.size() > 2) {
            if (!string_to_int(args[2], &_id)) {
                throw Exception(format("invalid resource ID {0}.", quote(args[2])));
            }
        }
        if (args.size() > 1) {
            _code.assign(args[1]);
        }
    }

    virtual void run(const ResourceFork& rsrc) {
        if (_argc == 1) {
            foreach (it, rsrc) {
                print(io::out, format("{0}\n", it->code()));
            }
        } else if (_argc == 2) {
            const ResourceType& type = rsrc.at(_code);
            foreach (it, type) {
                print(io::out, format("{0}\t{1}\n", it->id(), it->name()));
            }
        } else if (_argc == 3) {
            const ResourceEntry& entry = rsrc.at(_code).at(_id);
            print(io::out, format("{0}\t{1}\n", entry.id(), entry.name()));
        }
    }

  private:
    int _argc;
    String _code;
    int16_t _id;
};

class CatCommand : public Command {
  public:
    CatCommand(const vector<StringPiece>& args) {
        if (args.size() != 3) {
            throw Exception(format("wrong number of arguments to command \"cat\"."));
        }
        _code.assign(args[1]);
        if (!string_to_int(args[2], &_id)) {
            throw Exception(format("invalid resource ID {0}.", quote(args[2])));
        }
    }

    virtual void run(const ResourceFork& rsrc) {
        const ResourceEntry& entry = rsrc.at(_code).at(_id);
        BytesPiece data = entry.data();
        write(1, data.data(), data.size());
    }

  private:
    String _code;
    int16_t _id;
};

class ConvertCommand : public Command {
  public:
    ConvertCommand(const vector<StringPiece>& args) {
        if (args.size() != 3) {
            throw Exception(format("wrong number of arguments to command \"convert\"."));
        }
        _code.assign(args[1]);
        if (!string_to_int(args[2], &_id)) {
            throw Exception(format("invalid resource ID {0}.", quote(args[2])));
        }
    }

    virtual void run(const ResourceFork& rsrc) {
        const ResourceEntry& entry = rsrc.at(_code).at(_id);
        BytesPiece data = entry.data();
        Bytes converted;

        if (_code == "snd ") {
            Json snd = read_snd(data);
            write_aiff(snd, &converted);
        } else if (_code == "TEXT") {
            String string(macroman::decode(data));
            cr2nl(&string);
            converted.assign(utf8::encode(string));
        } else if (_code == "STR#") {
            Json list = read_string_list(data);
            String decoded_string;
            print_to(&decoded_string, pretty_print(list));
            converted.assign(utf8::encode(decoded_string));
        } else if (_code == "clut") {
            Json list = read_clut(data);
            String decoded_string;
            print_to(&decoded_string, pretty_print(list));
            converted.assign(utf8::encode(decoded_string));
        } else {
            print(io::err, format("warning: printing unknown resource type {0} as raw data.\n",
                        quote(_code)));
            converted.assign(data.data(), data.size());
        }
        write(1, converted.data(), converted.size());
    }

  private:
    String _code;
    int16_t _id;
};

class Source {
  public:
    virtual ~Source() { }
    virtual BytesPiece load() = 0;
};

class ResourceForkSource : public Source {
  public:
    ResourceForkSource(const StringPiece& arg)
        : _path(arg) { }

    virtual BytesPiece load() {
        _file.reset(new MappedFile(_path));
        return _file->data();
    }

  private:
    const String _path;
    scoped_ptr<MappedFile> _file;
};

class AppleSingleSource : public Source {
  public:
    AppleSingleSource(const StringPiece& arg)
        : _path(arg) { }

    virtual BytesPiece load() {
        _file.reset(new MappedFile(_path));
        _apple_single.reset(new AppleSingle(_file->data()));
        return _apple_single->at(AppleSingle::RESOURCE_FORK);
    }

  private:
    const String _path;
    scoped_ptr<MappedFile> _file;
    scoped_ptr<AppleSingle> _apple_single;
};

class ZipSource : public Source {
  public:
    ZipSource(const StringPiece& arg) {
        String arg_string(arg);
        size_t comma = arg_string.find(',');
        if (comma == String::npos) {
            throw Exception(format("invalid format to --zip-file {0}.", quote(arg_string)));
        }
        _archive_path.assign(StringPiece(arg_string).substr(0, comma));

        _file_path.append("__MACOSX/");
        _file_path.append(StringPiece(arg_string).substr(comma + 1));
        size_t loc = _file_path.rfind('/');
        _file_path.replace(loc, 1, "/._");
    }

    virtual BytesPiece load() {
        _archive.reset(new ZipArchive(_archive_path, 0));
        _file.reset(new ZipFileReader(_archive.get(), _file_path));
        _apple_double.reset(new AppleDouble(_file->data()));
        return _apple_double->at(AppleDouble::RESOURCE_FORK);
    }

  private:
    String _archive_path;
    String _file_path;
    scoped_ptr<ZipArchive> _archive;
    scoped_ptr<ZipFileReader> _file;
    scoped_ptr<AppleDouble> _apple_double;
};

int main(int argc, char** argv) {
    const String binary_name(utf8::decode(argv[0]));
    scoped_ptr<Command> command;
    scoped_ptr<Source> source;

    try {
        opterr = 0;
        const option longopts[] = {
            { "apple-single",   required_argument,  NULL,   'a' },
            { "resource-fork",  required_argument,  NULL,   'r' },
            { "zip-file",       required_argument,  NULL,   'z' },
            { NULL,             0,                  NULL,   0 }
        };

        while (true) {
            const char ch = getopt_long(argc, argv, "a:r:z:", longopts, NULL);
            if (ch == -1) {
                break;
            }

            String arg;
            if (optarg) {
                arg.assign(utf8::decode(optarg));
            }
            switch (ch) {
              case 'a':
                if (source.get() != NULL) {
                    throw Exception(format("more than one source specified."));
                }
                source.reset(new AppleSingleSource(arg));
                break;

              case 'r':
                if (source.get() != NULL) {
                    throw Exception(format("more than one source specified."));
                }
                source.reset(new ResourceForkSource(arg));
                break;

              case 'z':
                if (source.get() != NULL) {
                    throw Exception(format("more than one source specified."));
                }
                source.reset(new ZipSource(arg));
                break;

              default:
                {
                    String opt;
                    if (optopt == '\0') {
                        opt.assign(utf8::decode(argv[optind - 1]));
                    } else {
                        opt.assign(format("-{0}", char(optopt)));
                    }
                    throw Exception(format("unrecognized option {0}.", quote(opt)));
                }
                break;
            }
        }

        argc -= optind;
        argv += optind;
        if (argc < 1) {
            throw Exception(format("missing command."));
        }
        if (source.get() == NULL) {
            throw Exception(format("must provide a source."));
        }

        String storage;
        vector<StringPiece> args;
        for (int i = 0; i < argc; ++i) {
            String arg(utf8::decode(argv[i]));
            storage.append(arg);
            args.push_back(StringPiece(storage).substr(storage.size() - arg.size()));
        }

        if (args[0] == "ls") {
            command.reset(new LsCommand(args));
        } else if (args[0] == "cat") {
            command.reset(new CatCommand(args));
        } else if (args[0] == "convert") {
            command.reset(new ConvertCommand(args));
        } else {
            throw Exception(format("unknown command '{0}'.", args[0]));
        }
    } catch (Exception& e) {
        print(io::err, format(
                    "{0}: {1}\n"
                    "usage: {0} [OPTIONS] ls [TYPE [ID]]\n"
                    "       {0} [OPTIONS] cat TYPE ID\n"
                    "       {0} [OPTIONS] convert TYPE ID\n"
                    "options:\n"
                    "    -a|--apple-single=FILE\n"
                    "    -r|--resource-fork=FILE\n"
                    "    -z|--zip-file=ZIP,FILE\n",
                    basename(binary_name), e.message()));
        return 1;
    }

    try {
        ResourceFork rsrc(source->load());
        command->run(rsrc);
    } catch (Exception& e) {
        print(io::err, format("{0}: {1}\n", binary_name, e.message()));
        return 1;
    }

    return 0;
}

}  // namespace
}  // namespace rezin

int main(int argc, char** argv) {
    return rezin::main(argc, argv);
}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <string.h>

#include <string>
#include <jvmti.h>

#include "globals.h"
#include "profiler.h"
#include "controller.h"

#if defined(__APPLE__) || defined(__FreeBSD__)
#define GETENV_NEW_THREAD_ASYNC_UNSAFE
#endif

static ConfigurationOptions* CONFIGURATION = new ConfigurationOptions();
static Profiler* prof;
static Controller* controller;

// This has to be here, or the VM turns off class loading events.
// And AsyncGetCallTrace needs class loading events to be turned on!
void JNICALL OnClassLoad(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread,
        jclass klass) {
    IMPLICITLY_USE(jvmti_env);
    IMPLICITLY_USE(jni_env);
    IMPLICITLY_USE(thread);
    IMPLICITLY_USE(klass);
}

static void JNICALL CompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method,
                                       jint code_size, const void* code_addr,
                                       jint map_length, const jvmtiAddrLocationMap* map,
                                       const void* compile_info) {
    // Needed to enable DebugNonSafepoints info by default
}

// Calls GetClassMethods on a given class to force the creation of
// jmethodIDs of it.
void CreateJMethodIDsForClass(jvmtiEnv *jvmti, jclass klass) {
    jint method_count;
    JvmtiScopedPtr<jmethodID> methods(jvmti);
    jvmtiError e = jvmti->GetClassMethods(klass, &method_count, methods.GetRef());
    if (e != JVMTI_ERROR_NONE && e != JVMTI_ERROR_CLASS_NOT_PREPARED) {
        // JVMTI_ERROR_CLASS_NOT_PREPARED is okay because some classes may
        // be loaded but not prepared at this point.
        JvmtiScopedPtr<char> ksig(jvmti);
        JVMTI_ERROR_CLEANUP(
            jvmti->GetClassSignature(klass, ksig.GetRef(), NULL),
            ksig.AbandonBecauseOfError());
        logError("Failed to create method IDs for methods in class %s with error %d ",
                 ksig.Get(), e);
    }
}

void JNICALL OnVMInit(jvmtiEnv *jvmti, JNIEnv *jniEnv, jthread thread) {
    IMPLICITLY_USE(thread);
    // Forces the creation of jmethodIDs of the classes that had already
    // been loaded (eg java.lang.Object, java.lang.ClassLoader) and
    // OnClassPrepare() misses.
    jint class_count;
    JvmtiScopedPtr<jclass> classes(jvmti);
    JVMTI_ERROR((jvmti->GetLoadedClasses(&class_count, classes.GetRef())));
    jclass *classList = classes.Get();
    for (int i = 0; i < class_count; ++i) {
        jclass klass = classList[i];
        CreateJMethodIDsForClass(jvmti, klass);
    }

#ifndef GETENV_NEW_THREAD_ASYNC_UNSAFE
    if (CONFIGURATION->host != NULL && CONFIGURATION->port != NULL) {
        controller->start();
    }
#endif
}

void JNICALL OnClassPrepare(jvmtiEnv *jvmti_env, JNIEnv *jni_env,
        jthread thread, jclass klass) {
    IMPLICITLY_USE(jni_env);
    IMPLICITLY_USE(thread);
    // We need to do this to "prime the pump", as it were -- make sure
    // that all of the methodIDs have been initialized internally, for
    // AsyncGetCallTrace.  I imagine it slows down class loading a mite,
    // but honestly, how fast does class loading have to be?
    CreateJMethodIDsForClass(jvmti_env, klass);
}

void JNICALL OnVMDeath(jvmtiEnv *jvmti_env, JNIEnv *jni_env) {
    IMPLICITLY_USE(jvmti_env);
    IMPLICITLY_USE(jni_env);

    prof->stop();
}

static bool PrepareJvmti(jvmtiEnv *jvmti) {
    // Set the list of permissions to do the various internal VM things
    // we want to do.
    jvmtiCapabilities caps;

    memset(&caps, 0, sizeof(caps));
    caps.can_generate_all_class_hook_events = 1;

    caps.can_get_source_file_name = 1;
    caps.can_get_line_numbers = 1;
    caps.can_get_bytecodes = 1;
    caps.can_get_constant_pool = 1;
    caps.can_generate_compiled_method_load_events = 1;
#ifdef GETENV_NEW_THREAD_ASYNC_UNSAFE
    caps.can_generate_native_method_bind_events = 1;
#endif

    jvmtiCapabilities all_caps;
    int error;

    if (JVMTI_ERROR_NONE ==
            (error = jvmti->GetPotentialCapabilities(&all_caps))) {
        // This makes sure that if we need a capability, it is one of the
        // potential capabilities.  The technique isn't wonderful, but it
        // is compact and as likely to be compatible between versions as
        // anything else.
        char *has = reinterpret_cast<char *>(&all_caps);
        const char *should_have = reinterpret_cast<const char *>(&caps);
        for (int i = 0; i < sizeof(all_caps); i++) {
            if ((should_have[i] != 0) && (has[i] == 0)) {
                return false;
            }
        }

        // This adds the capabilities.
        JVMTI_ERROR_CLEANUP_RET(
            jvmti->AddCapabilities(&caps),
            false,
            logError("Failed to add capabilities with error %d\n", error))
    }
    return true;
}

sigset_t prof_signal_mask;

void (*actual_JVM_StartThread)(JNIEnv *, jthread) = NULL;

void JVM_StartThread_Interposer(JNIEnv *jni_env, jobject jthread) {
    pthread_sigmask(SIG_BLOCK, &prof_signal_mask, NULL);
    actual_JVM_StartThread(jni_env, jthread);
    pthread_sigmask(SIG_UNBLOCK, &prof_signal_mask, NULL);
}

//Set up interposition of calls to Thread::start0
void JNICALL OnNativeMethodBind(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread,
        jmethodID method, void *address, void **new_address_ptr) {
    if (actual_JVM_StartThread != NULL) {
        return;
    }

    char *name_ptr, *signature_ptr;

    int err = jvmti_env->GetMethodName(method, &name_ptr, &signature_ptr, NULL);
    if (err != JNI_OK) {
        logError("Error %i retrieving method name", err);
        return;
    }
    if (strcmp(name_ptr, "start0") == 0 && strcmp(signature_ptr, "()V") == 0) {
        jclass declaringClass;
        int err = jvmti_env->GetMethodDeclaringClass(method, &declaringClass);
        if (err != JNI_OK) {
            logError("Error %i retrieving class", err);
            jvmti_env->Deallocate((unsigned char *) name_ptr);
            jvmti_env->Deallocate((unsigned char *) signature_ptr);
            return;
        }
        jclass clazz = jni_env->GetObjectClass(declaringClass);
        jmethodID getSimpleNameMethod = jni_env->GetMethodID(clazz,
            "getSimpleName", "()Ljava/lang/String;");
        jstring jClassName = (jstring) jni_env->CallObjectMethod(declaringClass,
            getSimpleNameMethod);

        const char *className = jni_env->GetStringUTFChars(jClassName, 0);
        if (strcmp(className, "Thread") == 0) {
            *new_address_ptr = (void*) &JVM_StartThread_Interposer;
            actual_JVM_StartThread = (void (*)(JNIEnv *, jthread)) address;
        }
        jni_env->ReleaseStringUTFChars(jClassName, className);
    }
    jvmti_env->Deallocate((unsigned char *) name_ptr);
    jvmti_env->Deallocate((unsigned char *) signature_ptr);
}

volatile bool main_started = false;

void JNICALL OnThreadStart(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread) {
    if (!main_started) {
        jvmtiThreadInfo thread_info;
        int error = jvmti_env->GetThreadInfo(thread, &thread_info);
        if (error == JNI_OK) {
            if (strcmp(thread_info.name, "main") == 0) {
                main_started = true;
                if (CONFIGURATION->start)
                    prof->start(jni_env);
            }
        }
    }
    pthread_sigmask(SIG_UNBLOCK, &prof_signal_mask, NULL);
}

static bool RegisterJvmti(jvmtiEnv *jvmti) {
    sigemptyset(&prof_signal_mask);
    sigaddset(&prof_signal_mask, SIGPROF);
    // Create the list of callbacks to be called on given events.
    jvmtiEventCallbacks *callbacks = new jvmtiEventCallbacks();
    memset(callbacks, 0, sizeof(jvmtiEventCallbacks));

    callbacks->VMInit = &OnVMInit;
    callbacks->VMDeath = &OnVMDeath;

    callbacks->ClassLoad = &OnClassLoad;
    callbacks->ClassPrepare = &OnClassPrepare;

    callbacks->CompiledMethodLoad = &CompiledMethodLoad;

    callbacks->NativeMethodBind = &OnNativeMethodBind;
    callbacks->ThreadStart = &OnThreadStart;

    JVMTI_ERROR_RET(
            (jvmti->SetEventCallbacks(callbacks, sizeof(jvmtiEventCallbacks))),
            false);

    jvmtiEvent events[] = {JVMTI_EVENT_CLASS_LOAD, JVMTI_EVENT_CLASS_PREPARE,
            JVMTI_EVENT_VM_DEATH, JVMTI_EVENT_VM_INIT, JVMTI_EVENT_COMPILED_METHOD_LOAD
#ifdef GETENV_NEW_THREAD_ASYNC_UNSAFE
        ,JVMTI_EVENT_THREAD_START,
        JVMTI_EVENT_NATIVE_METHOD_BIND
#endif
    };

    size_t num_events = sizeof(events) / sizeof(jvmtiEvent);

    // Enable the callbacks to be triggered when the events occur.
    // Events are enumerated in jvmstatagent.h
    for (int i = 0; i < num_events; i++) {
        JVMTI_ERROR_RET(
                (jvmti->SetEventNotificationMode(JVMTI_ENABLE, events[i], NULL)),
                false);
    }

    return true;
}

static char *safe_copy_string(const char *value, const char *next) {
    size_t size = (next == 0) ? strlen(value) : (size_t) (next - value);
    char *dest = (char *) malloc((size + 1) * sizeof(char));

    strncpy(dest, value, size);
    dest[size] = '\0';

    return dest;
}

static void parseArguments(char *options, ConfigurationOptions &configuration) {
    char* next = options;
    for (char *key = options; next != NULL; key = next + 1) {
        char *value = strchr(key, '=');
        next = strchr(key, ',');
        if (value == NULL) {
            logError("WARN: No value for key %s\n", key);
            continue;
        } else {
            value++;
            if (strstr(key, "intervalMin") == key) {
                configuration.samplingIntervalMin = atoi(value);
            } else if (strstr(key, "intervalMax") == key) {
                configuration.samplingIntervalMax = atoi(value);
            } else if (strstr(key, "interval") == key) {
                configuration.samplingIntervalMin = configuration.samplingIntervalMax = atoi(value);
            } else if (strstr(key, "logPath") == key) {
                configuration.logFilePath = safe_copy_string(value, next);
            } else if (strstr(key, "start") == key) {
                configuration.start = atoi(value);
            } else if (strstr(key, "host") == key) {
                configuration.host = safe_copy_string(value, next);
            } else if (strstr(key, "port") == key) {
                configuration.port = safe_copy_string(value, next);
            } else {
                logError("WARN: Unknown configuration option: %s\n", key);
            }
        }
    }
}

AGENTEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
    IMPLICITLY_USE(reserved);
    int err;
    jvmtiEnv *jvmti;
    parseArguments(options, *CONFIGURATION);

    if ((err = (jvm->GetEnv(reinterpret_cast<void **>(&jvmti), JVMTI_VERSION))) !=
            JNI_OK) {
        logError("ERROR: JVMTI initialisation error %d\n", err);
        return 1;
    }

    /*
      JNIEnv *jniEnv;
      if ((err = (vm->GetEnv(reinterpret_cast<void **>(&jniEnv),
      JNI_VERSION_1_6))) != JNI_OK) {
        logError("JNI Error %d\n", err);
        return 1;
      }
      */

    if (!PrepareJvmti(jvmti)) {
        logError("ERROR: Failed to initialize JVMTI. Continuing...\n");
        return 0;
    }

    if (!RegisterJvmti(jvmti)) {
        logError("ERROR: Failed to enable JVMTI events. Continuing...\n");
        // We fail hard here because we may have failed in the middle of
        // registering callbacks, which will leave the system in an
        // inconsistent state.
        return 1;
    }

    Asgct::SetAsgct(Accessors::GetJvmFunction<ASGCTType>("AsyncGetCallTrace"));

    prof = new Profiler(jvm, jvmti, CONFIGURATION);
    controller = new Controller(jvm, jvmti, prof, CONFIGURATION);

    return 0;
}

AGENTEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) {
    IMPLICITLY_USE(vm);

    controller->stop();
}

void bootstrapHandle(int signum, siginfo_t *info, void *context) {
    prof->handle(signum, info, context);
}

void logError(const char *__restrict format, ...) {
    va_list arg;

    va_start(arg, format);
    vfprintf(stderr, format, arg);
    va_end(arg);
}

Profiler *getProfiler() {
    return prof;
}
<commit_msg>Fix crashes that can occur when a thread stops.<commit_after>#include <stdio.h>
#include <string.h>

#include <string>
#include <jvmti.h>

#include "globals.h"
#include "profiler.h"
#include "controller.h"

#if defined(__APPLE__) || defined(__FreeBSD__)
#define GETENV_NEW_THREAD_ASYNC_UNSAFE
#endif

static ConfigurationOptions* CONFIGURATION = new ConfigurationOptions();
static Profiler* prof;
static Controller* controller;

// This has to be here, or the VM turns off class loading events.
// And AsyncGetCallTrace needs class loading events to be turned on!
void JNICALL OnClassLoad(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread,
        jclass klass) {
    IMPLICITLY_USE(jvmti_env);
    IMPLICITLY_USE(jni_env);
    IMPLICITLY_USE(thread);
    IMPLICITLY_USE(klass);
}

static void JNICALL CompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method,
                                       jint code_size, const void* code_addr,
                                       jint map_length, const jvmtiAddrLocationMap* map,
                                       const void* compile_info) {
    // Needed to enable DebugNonSafepoints info by default
}

// Calls GetClassMethods on a given class to force the creation of
// jmethodIDs of it.
void CreateJMethodIDsForClass(jvmtiEnv *jvmti, jclass klass) {
    jint method_count;
    JvmtiScopedPtr<jmethodID> methods(jvmti);
    jvmtiError e = jvmti->GetClassMethods(klass, &method_count, methods.GetRef());
    if (e != JVMTI_ERROR_NONE && e != JVMTI_ERROR_CLASS_NOT_PREPARED) {
        // JVMTI_ERROR_CLASS_NOT_PREPARED is okay because some classes may
        // be loaded but not prepared at this point.
        JvmtiScopedPtr<char> ksig(jvmti);
        JVMTI_ERROR_CLEANUP(
            jvmti->GetClassSignature(klass, ksig.GetRef(), NULL),
            ksig.AbandonBecauseOfError());
        logError("Failed to create method IDs for methods in class %s with error %d ",
                 ksig.Get(), e);
    }
}

void JNICALL OnVMInit(jvmtiEnv *jvmti, JNIEnv *jniEnv, jthread thread) {
    IMPLICITLY_USE(thread);
    // Forces the creation of jmethodIDs of the classes that had already
    // been loaded (eg java.lang.Object, java.lang.ClassLoader) and
    // OnClassPrepare() misses.
    jint class_count;
    JvmtiScopedPtr<jclass> classes(jvmti);
    JVMTI_ERROR((jvmti->GetLoadedClasses(&class_count, classes.GetRef())));
    jclass *classList = classes.Get();
    for (int i = 0; i < class_count; ++i) {
        jclass klass = classList[i];
        CreateJMethodIDsForClass(jvmti, klass);
    }

#ifndef GETENV_NEW_THREAD_ASYNC_UNSAFE
    if (CONFIGURATION->host != NULL && CONFIGURATION->port != NULL) {
        controller->start();
    }
#endif
}

void JNICALL OnClassPrepare(jvmtiEnv *jvmti_env, JNIEnv *jni_env,
        jthread thread, jclass klass) {
    IMPLICITLY_USE(jni_env);
    IMPLICITLY_USE(thread);
    // We need to do this to "prime the pump", as it were -- make sure
    // that all of the methodIDs have been initialized internally, for
    // AsyncGetCallTrace.  I imagine it slows down class loading a mite,
    // but honestly, how fast does class loading have to be?
    CreateJMethodIDsForClass(jvmti_env, klass);
}

void JNICALL OnVMDeath(jvmtiEnv *jvmti_env, JNIEnv *jni_env) {
    IMPLICITLY_USE(jvmti_env);
    IMPLICITLY_USE(jni_env);

    prof->stop();
}

static bool PrepareJvmti(jvmtiEnv *jvmti) {
    // Set the list of permissions to do the various internal VM things
    // we want to do.
    jvmtiCapabilities caps;

    memset(&caps, 0, sizeof(caps));
    caps.can_generate_all_class_hook_events = 1;

    caps.can_get_source_file_name = 1;
    caps.can_get_line_numbers = 1;
    caps.can_get_bytecodes = 1;
    caps.can_get_constant_pool = 1;
    caps.can_generate_compiled_method_load_events = 1;
#ifdef GETENV_NEW_THREAD_ASYNC_UNSAFE
    caps.can_generate_native_method_bind_events = 1;
#endif

    jvmtiCapabilities all_caps;
    int error;

    if (JVMTI_ERROR_NONE ==
            (error = jvmti->GetPotentialCapabilities(&all_caps))) {
        // This makes sure that if we need a capability, it is one of the
        // potential capabilities.  The technique isn't wonderful, but it
        // is compact and as likely to be compatible between versions as
        // anything else.
        char *has = reinterpret_cast<char *>(&all_caps);
        const char *should_have = reinterpret_cast<const char *>(&caps);
        for (int i = 0; i < sizeof(all_caps); i++) {
            if ((should_have[i] != 0) && (has[i] == 0)) {
                return false;
            }
        }

        // This adds the capabilities.
        JVMTI_ERROR_CLEANUP_RET(
            jvmti->AddCapabilities(&caps),
            false,
            logError("Failed to add capabilities with error %d\n", error))
    }
    return true;
}

sigset_t prof_signal_mask;

void (*actual_JVM_StartThread)(JNIEnv *, jthread) = NULL;

void JVM_StartThread_Interposer(JNIEnv *jni_env, jobject jthread) {
    pthread_sigmask(SIG_BLOCK, &prof_signal_mask, NULL);
    actual_JVM_StartThread(jni_env, jthread);
    pthread_sigmask(SIG_UNBLOCK, &prof_signal_mask, NULL);
}

//Set up interposition of calls to Thread::start0
void JNICALL OnNativeMethodBind(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread,
        jmethodID method, void *address, void **new_address_ptr) {
    if (actual_JVM_StartThread != NULL) {
        return;
    }

    char *name_ptr, *signature_ptr;

    int err = jvmti_env->GetMethodName(method, &name_ptr, &signature_ptr, NULL);
    if (err != JNI_OK) {
        logError("Error %i retrieving method name", err);
        return;
    }
    if (strcmp(name_ptr, "start0") == 0 && strcmp(signature_ptr, "()V") == 0) {
        jclass declaringClass;
        int err = jvmti_env->GetMethodDeclaringClass(method, &declaringClass);
        if (err != JNI_OK) {
            logError("Error %i retrieving class", err);
            jvmti_env->Deallocate((unsigned char *) name_ptr);
            jvmti_env->Deallocate((unsigned char *) signature_ptr);
            return;
        }
        jclass clazz = jni_env->GetObjectClass(declaringClass);
        jmethodID getSimpleNameMethod = jni_env->GetMethodID(clazz,
            "getSimpleName", "()Ljava/lang/String;");
        jstring jClassName = (jstring) jni_env->CallObjectMethod(declaringClass,
            getSimpleNameMethod);

        const char *className = jni_env->GetStringUTFChars(jClassName, 0);
        if (strcmp(className, "Thread") == 0) {
            *new_address_ptr = (void*) &JVM_StartThread_Interposer;
            actual_JVM_StartThread = (void (*)(JNIEnv *, jthread)) address;
        }
        jni_env->ReleaseStringUTFChars(jClassName, className);
    }
    jvmti_env->Deallocate((unsigned char *) name_ptr);
    jvmti_env->Deallocate((unsigned char *) signature_ptr);
}

volatile bool main_started = false;

void JNICALL OnThreadStart(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread) {
    if (!main_started) {
        jvmtiThreadInfo thread_info;
        int error = jvmti_env->GetThreadInfo(thread, &thread_info);
        if (error == JNI_OK) {
            if (strcmp(thread_info.name, "main") == 0) {
                main_started = true;
                if (CONFIGURATION->start)
                    prof->start(jni_env);
            }
        }
    }
    pthread_sigmask(SIG_UNBLOCK, &prof_signal_mask, NULL);
}

void JNICALL OnThreadEnd(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread) {
    pthread_sigmask(SIG_BLOCK, &prof_signal_mask, NULL);
}

static bool RegisterJvmti(jvmtiEnv *jvmti) {
    sigemptyset(&prof_signal_mask);
    sigaddset(&prof_signal_mask, SIGPROF);
    // Create the list of callbacks to be called on given events.
    jvmtiEventCallbacks *callbacks = new jvmtiEventCallbacks();
    memset(callbacks, 0, sizeof(jvmtiEventCallbacks));

    callbacks->VMInit = &OnVMInit;
    callbacks->VMDeath = &OnVMDeath;

    callbacks->ClassLoad = &OnClassLoad;
    callbacks->ClassPrepare = &OnClassPrepare;

    callbacks->CompiledMethodLoad = &CompiledMethodLoad;

    callbacks->NativeMethodBind = &OnNativeMethodBind;
    callbacks->ThreadStart = &OnThreadStart;
    callbacks->ThreadEnd = &OnThreadEnd;

    JVMTI_ERROR_RET(
            (jvmti->SetEventCallbacks(callbacks, sizeof(jvmtiEventCallbacks))),
            false);

    jvmtiEvent events[] = {JVMTI_EVENT_CLASS_LOAD, JVMTI_EVENT_CLASS_PREPARE,
            JVMTI_EVENT_VM_DEATH, JVMTI_EVENT_VM_INIT, JVMTI_EVENT_COMPILED_METHOD_LOAD
#ifdef GETENV_NEW_THREAD_ASYNC_UNSAFE
        ,JVMTI_EVENT_THREAD_START,
        JVMTI_EVENT_THREAD_END,
        JVMTI_EVENT_NATIVE_METHOD_BIND
#endif
    };

    size_t num_events = sizeof(events) / sizeof(jvmtiEvent);

    // Enable the callbacks to be triggered when the events occur.
    // Events are enumerated in jvmstatagent.h
    for (int i = 0; i < num_events; i++) {
        JVMTI_ERROR_RET(
                (jvmti->SetEventNotificationMode(JVMTI_ENABLE, events[i], NULL)),
                false);
    }

    return true;
}

static char *safe_copy_string(const char *value, const char *next) {
    size_t size = (next == 0) ? strlen(value) : (size_t) (next - value);
    char *dest = (char *) malloc((size + 1) * sizeof(char));

    strncpy(dest, value, size);
    dest[size] = '\0';

    return dest;
}

static void parseArguments(char *options, ConfigurationOptions &configuration) {
    char* next = options;
    for (char *key = options; next != NULL; key = next + 1) {
        char *value = strchr(key, '=');
        next = strchr(key, ',');
        if (value == NULL) {
            logError("WARN: No value for key %s\n", key);
            continue;
        } else {
            value++;
            if (strstr(key, "intervalMin") == key) {
                configuration.samplingIntervalMin = atoi(value);
            } else if (strstr(key, "intervalMax") == key) {
                configuration.samplingIntervalMax = atoi(value);
            } else if (strstr(key, "interval") == key) {
                configuration.samplingIntervalMin = configuration.samplingIntervalMax = atoi(value);
            } else if (strstr(key, "logPath") == key) {
                configuration.logFilePath = safe_copy_string(value, next);
            } else if (strstr(key, "start") == key) {
                configuration.start = atoi(value);
            } else if (strstr(key, "host") == key) {
                configuration.host = safe_copy_string(value, next);
            } else if (strstr(key, "port") == key) {
                configuration.port = safe_copy_string(value, next);
            } else {
                logError("WARN: Unknown configuration option: %s\n", key);
            }
        }
    }
}

AGENTEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
    IMPLICITLY_USE(reserved);
    int err;
    jvmtiEnv *jvmti;
    parseArguments(options, *CONFIGURATION);

    if ((err = (jvm->GetEnv(reinterpret_cast<void **>(&jvmti), JVMTI_VERSION))) !=
            JNI_OK) {
        logError("ERROR: JVMTI initialisation error %d\n", err);
        return 1;
    }

    /*
      JNIEnv *jniEnv;
      if ((err = (vm->GetEnv(reinterpret_cast<void **>(&jniEnv),
      JNI_VERSION_1_6))) != JNI_OK) {
        logError("JNI Error %d\n", err);
        return 1;
      }
      */

    if (!PrepareJvmti(jvmti)) {
        logError("ERROR: Failed to initialize JVMTI. Continuing...\n");
        return 0;
    }

    if (!RegisterJvmti(jvmti)) {
        logError("ERROR: Failed to enable JVMTI events. Continuing...\n");
        // We fail hard here because we may have failed in the middle of
        // registering callbacks, which will leave the system in an
        // inconsistent state.
        return 1;
    }

    Asgct::SetAsgct(Accessors::GetJvmFunction<ASGCTType>("AsyncGetCallTrace"));

    prof = new Profiler(jvm, jvmti, CONFIGURATION);
    controller = new Controller(jvm, jvmti, prof, CONFIGURATION);

    return 0;
}

AGENTEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) {
    IMPLICITLY_USE(vm);

    controller->stop();
}

void bootstrapHandle(int signum, siginfo_t *info, void *context) {
    prof->handle(signum, info, context);
}

void logError(const char *__restrict format, ...) {
    va_list arg;

    va_start(arg, format);
    vfprintf(stderr, format, arg);
    va_end(arg);
}

Profiler *getProfiler() {
    return prof;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2012 Adam Murdoch
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/*
 * Apple specific functions.
 */
#if defined(__APPLE__)

#include "native.h"
#include "generic.h"
#include <string.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#include <unistd.h>
#include <sys/attr.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <mach/mach.h>

typedef struct vol_caps_buf {
    u_int32_t size;
    vol_capabilities_attr_t caps;
} vol_caps_buf_t;

/*
 * File system functions
 */
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileSystems(JNIEnv *env, jclass target, jobject info, jobject result) {
    int fs_count = getfsstat(NULL, 0, MNT_NOWAIT);
    if (fs_count < 0) {
        mark_failed_with_errno(env, "could not stat file systems", result);
        return;
    }

    size_t len = fs_count * sizeof(struct statfs);
    struct statfs* buf = (struct statfs*)malloc(len);
    if (getfsstat(buf, len, MNT_NOWAIT) < 0 ) {
        mark_failed_with_errno(env, "could not stat file systems", result);
        free(buf);
        return;
    }

    jclass info_class = env->GetObjectClass(info);
    jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZ)V");

    for (int i = 0; i < fs_count; i++) {
        jboolean caseSensitive = JNI_TRUE;
        jboolean casePreserving = JNI_TRUE;

        struct attrlist alist;
        memset(&alist, 0, sizeof(alist));
        alist.bitmapcount = ATTR_BIT_MAP_COUNT;
        alist.volattr = ATTR_VOL_CAPABILITIES | ATTR_VOL_INFO;
        vol_caps_buf_t buffer;

        // getattrlist requires the path to the actual mount point.
        int err = getattrlist(buf[i].f_mntonname, &alist, &buffer, sizeof(buffer), 0);
        if (err != 0) {
            mark_failed_with_errno(env, "could not determine file system attributes", result);
            break;
        }

        if (alist.volattr & ATTR_VOL_CAPABILITIES) {
            if ((buffer.caps.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_SENSITIVE)) {
                caseSensitive = (buffer.caps.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_SENSITIVE) != 0;
            }
            if ((buffer.caps.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_PRESERVING)) {
                casePreserving = (buffer.caps.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_PRESERVING) != 0;
            }
        }

        jstring mount_point = char_to_java(env, buf[i].f_mntonname, result);
        jstring file_system_type = char_to_java(env, buf[i].f_fstypename, result);
        jstring device_name = char_to_java(env, buf[i].f_mntfromname, result);
        jboolean remote = (buf[i].f_flags & MNT_LOCAL) == 0;
        env->CallVoidMethod(info, method, mount_point, file_system_type, device_name, remote, caseSensitive, casePreserving);
    }
    free(buf);
}

/**
 * Memory functions
 */
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_MemoryFunctions_getMemoryInfo(JNIEnv *env, jclass type, jobject dest, jobject result) {
    jclass destClass = env->GetObjectClass(dest);
    jmethodID mid = env->GetMethodID(destClass, "details", "(JJ)V");
    if (mid == NULL) {
        mark_failed_with_message(env, "could not find method", result);
        return;
    }

    // Get total physical memory
    int mib[2];
    mib[0] = CTL_HW;
    mib[1] = HW_MEMSIZE;
    int64_t total_memory = 0;
    size_t len = sizeof(total_memory);
    if (sysctl(mib, 2, &total_memory, &len, NULL, 0) != 0) {
        mark_failed_with_errno(env, "could not query memory size", result);
        return;
    }

    // Calculate available system memory
    // This is an approximation due to the Darwin VM model
    // free + inactive - speculative pages
    vm_size_t page_size;
    mach_port_t mach_port;
    mach_msg_type_number_t count;
    vm_statistics64_data_t vm_stats;

    mach_port = mach_host_self();
    count = sizeof(vm_stats) / sizeof(natural_t);
    if (KERN_SUCCESS != host_page_size(mach_port, &page_size)) {
        mark_failed_with_errno(env, "could not query page size", result);
        return;
    }
    if (KERN_SUCCESS != host_statistics64(mach_port, HOST_VM_INFO, (host_info64_t)&vm_stats, &count)) {
        mark_failed_with_errno(env, "could not query host statistics", result);
        return;
    }
    long long available_memory = ((int64_t)vm_stats.free_count
                                 + (int64_t)vm_stats.inactive_count
                                 - (int64_t)vm_stats.speculative_count)
                                 * (int64_t)page_size;

    env->CallVoidMethod(dest, mid, (jlong)total_memory, (jlong)available_memory);
}

#endif
<commit_msg>Minor edits to main/apple.cpp for memory info<commit_after>/*
 * Copyright 2012 Adam Murdoch
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/*
 * Apple specific functions.
 */
#if defined(__APPLE__)

#include "native.h"
#include "generic.h"
#include <string.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#include <unistd.h>
#include <sys/attr.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <mach/mach.h>

typedef struct vol_caps_buf {
    u_int32_t size;
    vol_capabilities_attr_t caps;
} vol_caps_buf_t;

/*
 * File system functions
 */
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileSystems(JNIEnv *env, jclass target, jobject info, jobject result) {
    int fs_count = getfsstat(NULL, 0, MNT_NOWAIT);
    if (fs_count < 0) {
        mark_failed_with_errno(env, "could not stat file systems", result);
        return;
    }

    size_t len = fs_count * sizeof(struct statfs);
    struct statfs* buf = (struct statfs*)malloc(len);
    if (getfsstat(buf, len, MNT_NOWAIT) < 0 ) {
        mark_failed_with_errno(env, "could not stat file systems", result);
        free(buf);
        return;
    }

    jclass info_class = env->GetObjectClass(info);
    jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZ)V");

    for (int i = 0; i < fs_count; i++) {
        jboolean caseSensitive = JNI_TRUE;
        jboolean casePreserving = JNI_TRUE;

        struct attrlist alist;
        memset(&alist, 0, sizeof(alist));
        alist.bitmapcount = ATTR_BIT_MAP_COUNT;
        alist.volattr = ATTR_VOL_CAPABILITIES | ATTR_VOL_INFO;
        vol_caps_buf_t buffer;

        // getattrlist requires the path to the actual mount point.
        int err = getattrlist(buf[i].f_mntonname, &alist, &buffer, sizeof(buffer), 0);
        if (err != 0) {
            mark_failed_with_errno(env, "could not determine file system attributes", result);
            break;
        }

        if (alist.volattr & ATTR_VOL_CAPABILITIES) {
            if ((buffer.caps.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_SENSITIVE)) {
                caseSensitive = (buffer.caps.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_SENSITIVE) != 0;
            }
            if ((buffer.caps.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_PRESERVING)) {
                casePreserving = (buffer.caps.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_PRESERVING) != 0;
            }
        }

        jstring mount_point = char_to_java(env, buf[i].f_mntonname, result);
        jstring file_system_type = char_to_java(env, buf[i].f_fstypename, result);
        jstring device_name = char_to_java(env, buf[i].f_mntfromname, result);
        jboolean remote = (buf[i].f_flags & MNT_LOCAL) == 0;
        env->CallVoidMethod(info, method, mount_point, file_system_type, device_name, remote, caseSensitive, casePreserving);
    }
    free(buf);
}

/**
 * Memory functions
 */
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_MemoryFunctions_getMemoryInfo(JNIEnv *env, jclass type, jobject dest, jobject result) {
    jclass destClass = env->GetObjectClass(dest);
    jmethodID mid = env->GetMethodID(destClass, "details", "(JJ)V");
    if (mid == NULL) {
        mark_failed_with_message(env, "could not find method", result);
        return;
    }

    // Get total physical memory
    int mib[2];
    mib[0] = CTL_HW;
    mib[1] = HW_MEMSIZE;
    int64_t total_memory = 0;
    size_t len = sizeof(total_memory);
    if (sysctl(mib, 2, &total_memory, &len, NULL, 0) != 0) {
        mark_failed_with_errno(env, "could not query memory size", result);
        return;
    }

    // Get VM stats
    vm_size_t page_size;
    mach_port_t mach_port;
    mach_msg_type_number_t count;
    vm_statistics64_data_t vm_stats;

    mach_port = mach_host_self();
    count = HOST_VM_INFO64_COUNT;
    if (KERN_SUCCESS != host_page_size(mach_port, &page_size)) {
        mark_failed_with_errno(env, "could not query page size", result);
        return;
    }
    if (KERN_SUCCESS != host_statistics64(mach_port, HOST_VM_INFO, (host_info64_t)&vm_stats, &count)) {
        mark_failed_with_errno(env, "could not query host statistics", result);
        return;
    }

    // Calculate available memory
    long long available_memory = ((int64_t)vm_stats.free_count
                                 + (int64_t)vm_stats.inactive_count
                                 - (int64_t)vm_stats.speculative_count)
                                 * (int64_t)page_size;

    // Feed Java with details
    env->CallVoidMethod(dest, mid, (jlong)total_memory, (jlong)available_memory);
}

#endif
<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>
#include <vector>
#include <queue>
#include <stdio.h>
#include <cstring>
#include <sys/wait.h>
#include <sys/types.h>

using namespace std;
using namespace boost;

class Connectors    //abstract base class so we can dynamically call run
{
    public:
        virtual bool run(bool state) = 0;
};

class Semicolon : public Connectors
{
    private:
       vector<char *> vctr;
    public:
    Semicolon(vector<string> v) : vctr(50)  //constructor creates char* vector
    {                                       //which stores one command+params
        vctr.reserve(v.size());             //in a semicolon object
        for (unsigned i = 0; i < v.size(); ++i)
        {
            vctr[i] = const_cast<char*>(v[i].c_str());
        }
        vctr.push_back(NULL);   //makes sure the command ends with a null char
    }                           //so execvp can determine the end
    virtual bool run(bool state)
    {
        char **pointer = &vctr[0];  //points to vctr we'll use for execvp

        string ex = "exit";
        string p = pointer[0];
        if (p == ex) // check if the command entered is exit
        {
            exit(0);
        }

        pid_t c_pid, pid;
        int status;
        c_pid = fork();

        if (c_pid < 0)      //checks if fork failed
        {
            perror("fork failed");
            state = 0;
            return state;
        }
        else if (c_pid == 0)    //if we're in the child, call execvp
        {
            execvp(pointer[0], pointer);
            perror("execvp failed");
            exit(127);
        }
        else //if in the parent, wait for child to terminate
        {
            //checks if wait fails
            if ((pid = wait(&status)) < 0)
            {
                perror("wait failed");
                state = 0;
                return state;
            }
            //checks whether the child succeeded or not
            //returns the state accordingly
            if (status == 0)
            {
                state = 1; //if it succeeded, set the state to true
                return state;
            }
            else
            {
                state = 0;
                return state;
            }
        }
        return 0;
    }
};

class And : public Connectors
{
    private:
       vector<char *> vctr;
    public:
    And(vector<string> v) : vctr(50)
    {
        vctr.reserve(v.size()); //store one command+param of type And
        for (unsigned i = 0; i < v.size(); ++i)
        {
            vctr[i] = const_cast<char*>(v[i].c_str());
        }
        vctr.push_back(NULL); //end with null char
    }
    virtual bool run(bool state)
    {
        if (state != 1) //return if the previous command failed
        {
            return state;
        }

        char **pointer = &vctr[0];  //points to vctr we'll use for execvp

        string ex = "exit";
        string p = pointer[0];
        if (p == ex) // check if the command entered is exit
        {
            exit(0);
        }

        pid_t c_pid, pid;
        int status;
        c_pid = fork();

        if (c_pid < 0)      //checks if fork failed
        {
            perror("fork failed");
            state = 0;
            return state;
        }
        else if (c_pid == 0)    //if we're in the child, call execvp
        {
            execvp(pointer[0], pointer);
            perror("execvp failed");
            exit(127);
        }
        else //if in the parent, wait for child to terminate
        {
            //checks if wait fails
            if ((pid = wait(&status)) < 0)
            {
                perror("wait failed");
                state = 0;
                return state;
            }
            //checks whether the child succeeded or not
            //outputs the state accordingly
            if (status == 0)
            {
                state = 1; //if it succeeded, set the state to true
                return state;
            }
            else
            {
                state = 0;
                return state;
            }
        }
        return 0;
    }
};

class Or : public Connectors
{
    private:
       vector<char *> vctr;
    public:
    Or(vector<string> v) : vctr(50) //stores one command+params of type Or
    {
        vctr.reserve(v.size());
        for (unsigned i = 0; i < v.size(); ++i)
        {
            vctr[i] = const_cast<char*>(v[i].c_str());
        }
        vctr.push_back(NULL); //end with null
    }
    virtual bool run(bool state)
    {
        if (state != 0) //return if the previous command succeeded
        {
            return state;
        }

        char **pointer = &vctr[0];  //points to vctr we'll use for execvp

        string ex = "exit";
        string p = pointer[0];
        if (p == ex) // check if the command entered is exit
        {
            exit(0);
        }

        pid_t c_pid, pid;
        int status;
        c_pid = fork();

        if (c_pid < 0)      //checks if fork failed
        {
            perror("fork failed");
            state = 0;
            return state;
        }
        else if (c_pid == 0)    //if we're in the child, call execvp
        {
            execvp(pointer[0], pointer);
            perror("execvp failed");
            exit(127);
        }
        else //if in the parent, wait for child to terminate
        {
            //checks if wait fails
            if ((pid = wait(&status)) < 0)
            {
                perror("wait failed");
                state = 0;
                return state;
            }
            //checks whether the child succeeded or not
            //outputs the state accodingly
            if (status == 0)
            {
                state = 1; //if it succeeded, set the state to true
                return state;
            }
            else
            {
                state = 0;
                return state;
            }
        }
        return 0;
    }
};

int main()
{
    int w = 0;
    while (w == 0)
    {
        // extra credit part
        char *username;
        int host;
        if ((username = getlogin()) != NULL) // get the username
        {
            char name[101];
            int len = 100;
            if ((host = gethostname(name, len)) == 0) // get the hostname
            {
                cout << username << "@" << name << "$ ";
            }
            else
            {
                 cout << "$ ";           //outputs terminal $
            }
        }
        else
        {
            cout << "$ ";
        }

        string input;
        getline(cin, input);    //gets user input

        //containers we'll use for storage
        vector< vector<string> > v;
        vector<string> t;
        vector<Connectors*> objects;
        queue<string> q;

        int column = 0;

        //creates tokenizer and char separator to parse input
        typedef tokenizer<char_separator<char> > tokenizer;
        char_separator<char> sep(" ", ";#");
        tokenizer tokens(input, sep);

        bool flag = 0; //flag to check when to start a new column

        //holds commands in a 2d vector

        for (tokenizer::iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
        {
            if ((*itr == ";") || (*itr == "||") || (*itr == "&&"))
            {
                q.push(*itr); //pushes connector into queue
                column = column + 1; //increments column
                flag = 0;
            }
            else if (*itr == "#")
            {
                break;          //if there's a comment, break out of loop
            }
            else
            {
                if (!flag)
                {
                    v.push_back(t); //starts a new column
                    v.at(column).push_back(*itr);
                    flag = 1;
                }
                else
                { //push value into position
                    v.at(column).push_back(*itr);
                }
            }
        }

        //checks the contents of v
        //for (unsigned i = 0; i < v.size(); ++i)
        //{
           //for (unsigned j = 0; j < v.at(i).size(); ++j)
           //{
                //cout << v.at(i).at(j);
           //}

        //}

        //this part of the code creates a temp vector current which holds
        //a single command+param chunk at a time
        //then determines the connector previous to the command its told to run
        //it then creates the corresponding connector class type object
        //and pushes the new object into a vector of Connectors pointers
        bool first = 1;
        vector<string> current;

        for (unsigned i = 0; i < v.size(); ++i)
        {
            for (unsigned j = 0; j < v.at(i).size(); ++j)
            {
                current.push_back(v.at(i).at(j));
            }
            if (!q.empty() && first != 1)
            {
                if (q.front() == ";")
                {
                    objects.push_back(new Semicolon(current));
                }

                if (q.front() == "||")
                {
                    objects.push_back(new Or(current));
                }

                if (q.front() == "&&")
                {
                    objects.push_back(new And(current));
                }
                q.pop();
            }
            if (first == 1)
            {
                objects.push_back(new Semicolon(current));
                first = 0;
            }
            current.clear();
        }
        bool beg = 0;
        bool durr = 0;
        //this loop goes through the object vector and calls run on each
        //object, dynamically calling the run of the class type
        //cout << "Size: " << objects.size()  << endl;
        for (unsigned i = 0; i < objects.size(); ++i)
        {
            //cout << "Curr size: " << objects.size() << endl;
            durr = objects.at(i)->run(beg);
            //cout << "State after run: " << durr << endl;
            beg = durr;
        }
    }
    return 0;
}

<commit_msg>fixed single and no spaces in connectors<commit_after>#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>
#include <vector>
#include <queue>
#include <stdio.h>
#include <cstring>
#include <sys/wait.h>
#include <sys/types.h>

using namespace std;
using namespace boost;

class Connectors    //abstract base class so we can dynamically call run
{
    public:
        virtual bool run(bool state) = 0;
};

class Semicolon : public Connectors
{
    private:
       vector<char *> vctr;
    public:
    Semicolon(vector<string> v) : vctr(50)  //constructor creates char* vector
    {                                       //which stores one command+params
        vctr.reserve(v.size());             //in a semicolon object
        for (unsigned i = 0; i < v.size(); ++i)
        {
            vctr[i] = const_cast<char*>(v[i].c_str());
        }
        vctr.push_back(NULL);   //makes sure the command ends with a null char
    }                           //so execvp can determine the end
    virtual bool run(bool state)
    {
        char **pointer = &vctr[0];  //points to vctr we'll use for execvp

        string ex = "exit";
        string p = pointer[0];
        if (p == ex) // check if the command entered is exit
        {
            exit(0);
        }

        pid_t c_pid, pid;
        int status;
        c_pid = fork();

        if (c_pid < 0)      //checks if fork failed
        {
            perror("fork failed");
            state = 0;
            return state;
        }
        else if (c_pid == 0)    //if we're in the child, call execvp
        {
            execvp(pointer[0], pointer);
            perror("execvp failed");
            exit(127);
        }
        else //if in the parent, wait for child to terminate
        {
            //checks if wait fails
            if ((pid = waitpid(c_pid, &status, 0)) < 0)
            {
                perror("wait failed");
                state = 0;
                return state;
            }
            //checks whether the child succeeded or not
            //returns the state accordingly
            if (status == 0)
            {
                state = 1; //if it succeeded, set the state to true
                return state;
            }
            else
            {
                state = 0;
                return state;
            }
        }
        return 0;
    }
};

class And : public Connectors
{
    private:
       vector<char *> vctr;
    public:
    And(vector<string> v) : vctr(50)
    {
        vctr.reserve(v.size()); //store one command+param of type And
        for (unsigned i = 0; i < v.size(); ++i)
        {
            vctr[i] = const_cast<char*>(v[i].c_str());
        }
        vctr.push_back(NULL); //end with null char
    }
    virtual bool run(bool state)
    {
        if (state != 1) //return if the previous command failed
        {
            return state;
        }

        char **pointer = &vctr[0];  //points to vctr we'll use for execvp

        string ex = "exit";
        string p = pointer[0];
        if (p == ex) // check if the command entered is exit
        {
            exit(0);
        }

        pid_t c_pid, pid;
        int status;
        c_pid = fork();

        if (c_pid < 0)      //checks if fork failed
        {
            perror("fork failed");
            state = 0;
            return state;
        }
        else if (c_pid == 0)    //if we're in the child, call execvp
        {
            execvp(pointer[0], pointer);
            perror("execvp failed");
            exit(127);
        }
        else //if in the parent, wait for child to terminate
        {
            //checks if wait fails
            if ((pid = waitpid(c_pid, &status, 0)) < 0)
            {
                perror("wait failed");
                state = 0;
                return state;
            }
            //checks whether the child succeeded or not
            //outputs the state accordingly
            if (status == 0)
            {
                state = 1; //if it succeeded, set the state to true
                return state;
            }
            else
            {
                state = 0;
                return state;
            }
        }
        return 0;
    }
};

class Or : public Connectors
{
    private:
       vector<char *> vctr;
    public:
    Or(vector<string> v) : vctr(50) //stores one command+params of type Or
    {
        vctr.reserve(v.size());
        for (unsigned i = 0; i < v.size(); ++i)
        {
            vctr[i] = const_cast<char*>(v[i].c_str());
        }
        vctr.push_back(NULL); //end with null
    }
    virtual bool run(bool state)
    {
        if (state != 0) //return if the previous command succeeded
        {
            return state;
        }

        char **pointer = &vctr[0];  //points to vctr we'll use for execvp

        string ex = "exit";
        string p = pointer[0];
        if (p == ex) // check if the command entered is exit
        {
            exit(0);
        }

        pid_t c_pid, pid;
        int status;
        c_pid = fork();

        if (c_pid < 0)      //checks if fork failed
        {
            perror("fork failed");
            state = 0;
            return state;
        }
        else if (c_pid == 0)    //if we're in the child, call execvp
        {
            execvp(pointer[0], pointer);
            perror("execvp failed");
            exit(127);
        }
        else //if in the parent, wait for child to terminate
        {
            //checks if wait fails
            if ((pid = waitpid(c_pid, &status, 0)) < 0)
            {
                perror("wait failed");
                state = 0;
                return state;
            }
            //checks whether the child succeeded or not
            //outputs the state accodingly
            if (status == 0)
            {
                state = 1; //if it succeeded, set the state to true
                return state;
            }
            else
            {
                state = 0;
                return state;
            }
        }
        return 0;
    }
};

int main()
{
    int w = 0;
    while (w == 0)
    {
        // extra credit part
        char *username;
        int host;
        if ((username = getlogin()) != NULL) // get the username
        {
            char name[101];
            int len = 100;
            if ((host = gethostname(name, len)) == 0) // get the hostname
            {
                cout << username << "@" << name << "$ ";
            }
            else
            {
                 cout << "$ ";           //outputs terminal $
            }
        }
        else
        {
            cout << "$ ";
        }

        string input;
        getline(cin, input);    //gets user input

        //containers we'll use for storage
        vector< vector<string> > v;
        vector<string> t;
        vector<Connectors*> objects;
        queue<string> q;

        int column = 0;

        //creates tokenizer and char separator to parse input
        typedef tokenizer<char_separator<char> > tokenizer;
        char_separator<char> sep(" ", ";#|&");
        tokenizer tokens(input, sep);

        bool lastVal = 0;
        for (tokenizer::iterator check = tokens.begin(); check != tokens.end(); ++check)
        {
            tokenizer::iterator count = check;
            ++count;
            if (count == tokens.end())
            {
                if (*check == "|" || *check == "&")
                {
                    lastVal = 1;
                }
            }
        }

        if (lastVal == 1)
        {
            cout << "end of input cannot be a connector" << endl;
            continue;
        }

        bool flag = 0; //flag to check when to start a new column
        bool wrong = 0;
        //holds commands in a 2d vector

        for (tokenizer::iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
        {
            tokenizer::iterator temp = itr;
            if (*itr == ";")
            {
                ++temp;
                if (temp != tokens.end())
                {
                    if (*temp == *itr)
                    {
                        cout << "cannot have multiple semicolons" << endl;
                        wrong = 1;
                        break;
                    }
                }
                q.push(*itr);
                column = column + 1;
                flag = 0;
            }

            else if ((*itr == "|") || (*itr == "&"))
            {
                ++temp;
                if (*temp != *itr)
                {
                    cout << "cannot enter single connector" << endl;
                    wrong = 1;
                    break;
                }
                ++temp;
                if (*temp == *itr)
                {
                    cout << "cannot enter 3+ connector" << endl;
                    wrong = 1;
                    break;
                }

                q.push(*itr); //pushes connector into queue
                column = column + 1; //increments column
                flag = 0;
                ++itr;  //extra increments itr to not test second connector in pair
            }
            else if (*itr == "#")
            {
                break;          //if there's a comment, break out of loop
            }
            else
            {
                if (!flag)
                {
                    v.push_back(t); //starts a new column
                    v.at(column).push_back(*itr);
                    flag = 1;
                }
                else
                { //push value into position
                    v.at(column).push_back(*itr);
                }
            }
        }

        //checks the contents of v
        //for (unsigned i = 0; i < v.size(); ++i)
        //{
           //for (unsigned j = 0; j < v.at(i).size(); ++j)
           //{
                //cout << v.at(i).at(j);
           //}

        //}

        if (wrong == 1)
        {
            continue;
        }

        //this part of the code creates a temp vector current which holds
        //a single command+param chunk at a time
        //then determines the connector previous to the command its told to run
        //it then creates the corresponding connector class type object
        //and pushes the new object into a vector of Connectors pointers
        bool first = 1;
        vector<string> current;

        for (unsigned i = 0; i < v.size(); ++i)
        {
            for (unsigned j = 0; j < v.at(i).size(); ++j)
            {
                current.push_back(v.at(i).at(j));
            }
            if (!q.empty() && first != 1)
            {
                if (q.front() == ";")
                {
                    objects.push_back(new Semicolon(current));
                }

                if (q.front() == "|")
                {
                    objects.push_back(new Or(current));
                }

                if (q.front() == "&")
                {
                    objects.push_back(new And(current));
                }
                q.pop();
            }
            if (first == 1)
            {
                objects.push_back(new Semicolon(current));
                first = 0;
            }
            current.clear();
        }
        bool beg = 0;
        bool durr = 0;
        //this loop goes through the object vector and calls run on each
        //object, dynamically calling the run of the class type
        //cout << "Size: " << objects.size()  << endl;
        for (unsigned i = 0; i < objects.size(); ++i)
        {
            //cout << "Curr size: " << objects.size() << endl;
            durr = objects.at(i)->run(beg);
            //cout << "State after run: " << durr << endl;
            beg = durr;
        }
    }
    return 0;
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Andreas Hansson
 */

#include "mem/addr_mapper.hh"

AddrMapper::AddrMapper(const AddrMapperParams* p)
    : MemObject(p),
      masterPort(name() + "-master", *this),
      slavePort(name() + "-slave", *this)
{
}

void
AddrMapper::init()
{
    if (!slavePort.isConnected() || !masterPort.isConnected())
        fatal("Address mapper is not connected on both sides.\n");

    if ((slavePort.peerBlockSize() != masterPort.peerBlockSize()) &&
        slavePort.peerBlockSize() && masterPort.peerBlockSize())
        fatal("Slave port size %d, master port size %d \n "
              "don't have the same block size... Not supported.\n",
              slavePort.peerBlockSize(), masterPort.peerBlockSize());
}

BaseMasterPort&
AddrMapper::getMasterPort(const std::string& if_name, PortID idx)
{
    if (if_name == "master") {
        return masterPort;
    } else {
        return MemObject::getMasterPort(if_name, idx);
    }
}

BaseSlavePort&
AddrMapper::getSlavePort(const std::string& if_name, PortID idx)
{
    if (if_name == "slave") {
        return slavePort;
    } else {
        return MemObject::getSlavePort(if_name, idx);
    }
}

void
AddrMapper::recvFunctional(PacketPtr pkt)
{
    Addr orig_addr = pkt->getAddr();
    pkt->setAddr(remapAddr(orig_addr));
    masterPort.sendFunctional(pkt);
    pkt->setAddr(orig_addr);
}

void
AddrMapper::recvFunctionalSnoop(PacketPtr pkt)
{
    Addr orig_addr = pkt->getAddr();
    pkt->setAddr(remapAddr(orig_addr));
    slavePort.sendFunctionalSnoop(pkt);
    pkt->setAddr(orig_addr);
}

Tick
AddrMapper::recvAtomic(PacketPtr pkt)
{
    Addr orig_addr = pkt->getAddr();
    pkt->setAddr(remapAddr(orig_addr));
    Tick ret_tick =  masterPort.sendAtomic(pkt);
    pkt->setAddr(orig_addr);
    return ret_tick;
}

Tick
AddrMapper::recvAtomicSnoop(PacketPtr pkt)
{
    Addr orig_addr = pkt->getAddr();
    pkt->setAddr(remapAddr(orig_addr));
    Tick ret_tick = slavePort.sendAtomicSnoop(pkt);
    pkt->setAddr(orig_addr);
    return ret_tick;
}

bool
AddrMapper::recvTimingReq(PacketPtr pkt)
{
    Addr orig_addr = pkt->getAddr();
    bool needsResponse = pkt->needsResponse();
    bool memInhibitAsserted = pkt->memInhibitAsserted();
    Packet::SenderState* senderState = pkt->senderState;

    if (needsResponse && !memInhibitAsserted) {
        pkt->senderState = new AddrMapperSenderState(senderState, orig_addr);
    }

    pkt->setAddr(remapAddr(orig_addr));

    // Attempt to send the packet (always succeeds for inhibited
    // packets)
    bool successful = masterPort.sendTimingReq(pkt);

    // If not successful, restore the sender state
    if (!successful && needsResponse) {
        delete pkt->senderState;
        pkt->senderState = senderState;
    }

    return successful;
}

bool
AddrMapper::recvTimingResp(PacketPtr pkt)
{
    AddrMapperSenderState* receivedState =
        dynamic_cast<AddrMapperSenderState*>(pkt->senderState);

    // Restore initial sender state
    if (receivedState == NULL)
        panic("AddrMapper %s got a response without sender state\n",
              name());

    Addr remapped_addr = pkt->getAddr();

    // Restore the state and address
    pkt->senderState = receivedState->origSenderState;
    pkt->setAddr(receivedState->origAddr);

    // Attempt to send the packet
    bool successful = slavePort.sendTimingResp(pkt);

    // If packet successfully sent, delete the sender state, otherwise
    // restore state
    if (successful) {
        delete receivedState;
    } else {
        // Don't delete anything and let the packet look like we did
        // not touch it
        pkt->senderState = receivedState;
        pkt->setAddr(remapped_addr);
    }
    return successful;
}

void
AddrMapper::recvTimingSnoopReq(PacketPtr pkt)
{
    slavePort.sendTimingSnoopReq(pkt);
}

bool
AddrMapper::recvTimingSnoopResp(PacketPtr pkt)
{
    return masterPort.sendTimingSnoopResp(pkt);
}

bool
AddrMapper::isSnooping() const
{
    if (slavePort.isSnooping())
        fatal("AddrMapper doesn't support remapping of snooping requests\n");
    return false;
}

unsigned
AddrMapper::deviceBlockSizeMaster()
{
    return slavePort.peerBlockSize();
}

unsigned
AddrMapper::deviceBlockSizeSlave()
{
    return masterPort.peerBlockSize();
}

void
AddrMapper::recvRetryMaster()
{
    slavePort.sendRetry();
}

void
AddrMapper::recvRetrySlave()
{
    masterPort.sendRetry();
}

void
AddrMapper::recvRangeChange()
{
    slavePort.sendRangeChange();
}

RangeAddrMapper::RangeAddrMapper(const RangeAddrMapperParams* p) :
    AddrMapper(p),
    originalRanges(p->original_ranges),
    remappedRanges(p->remapped_ranges)
{
    if (originalRanges.size() != remappedRanges.size())
        fatal("AddrMapper: original and shadowed range list must "
              "be same size\n");

    for (size_t x = 0; x < originalRanges.size(); x++) {
        if (originalRanges[x].size() != remappedRanges[x].size())
            fatal("AddrMapper: original and shadowed range list elements"
                  " aren't all of the same size\n");
    }
}

RangeAddrMapper*
RangeAddrMapperParams::create()
{
    return new RangeAddrMapper(this);
}

Addr
RangeAddrMapper::remapAddr(Addr addr) const
{
    for (int i = 0; i < originalRanges.size(); ++i) {
        if (originalRanges[i].contains(addr)) {
            Addr offset = addr - originalRanges[i].start();
            return offset + remappedRanges[i].start();
        }
    }

    return addr;
}

AddrRangeList
RangeAddrMapper::getAddrRanges() const
{
    AddrRangeList ranges;
    AddrRangeList actualRanges = masterPort.getAddrRanges();

    for (AddrRangeIter r = actualRanges.begin(); r != actualRanges.end(); ++r) {
        AddrRange range = *r;

        for (int j = 0; j < originalRanges.size(); ++j) {
            if (range.intersects(originalRanges[j]))
                fatal("Cannot remap range that intersects the original"
                      " ranges but are not a subset.\n");
            if (range.isSubset(originalRanges[j])) {
                // range is a subset
                Addr offset = range.start() - originalRanges[j].start();
                Addr start = range.start() - offset;
                ranges.push_back(AddrRange(start, start + range.size() - 1));
            } else {
                ranges.push_back(range);
            }
        }
    }

    return ranges;
}

<commit_msg>mem: Skip address mapper range checks to allow more flexibility<commit_after>/*
 * Copyright (c) 2012 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Andreas Hansson
 */

#include "mem/addr_mapper.hh"

AddrMapper::AddrMapper(const AddrMapperParams* p)
    : MemObject(p),
      masterPort(name() + "-master", *this),
      slavePort(name() + "-slave", *this)
{
}

void
AddrMapper::init()
{
    if (!slavePort.isConnected() || !masterPort.isConnected())
        fatal("Address mapper is not connected on both sides.\n");

    if ((slavePort.peerBlockSize() != masterPort.peerBlockSize()) &&
        slavePort.peerBlockSize() && masterPort.peerBlockSize())
        fatal("Slave port size %d, master port size %d \n "
              "don't have the same block size... Not supported.\n",
              slavePort.peerBlockSize(), masterPort.peerBlockSize());
}

BaseMasterPort&
AddrMapper::getMasterPort(const std::string& if_name, PortID idx)
{
    if (if_name == "master") {
        return masterPort;
    } else {
        return MemObject::getMasterPort(if_name, idx);
    }
}

BaseSlavePort&
AddrMapper::getSlavePort(const std::string& if_name, PortID idx)
{
    if (if_name == "slave") {
        return slavePort;
    } else {
        return MemObject::getSlavePort(if_name, idx);
    }
}

void
AddrMapper::recvFunctional(PacketPtr pkt)
{
    Addr orig_addr = pkt->getAddr();
    pkt->setAddr(remapAddr(orig_addr));
    masterPort.sendFunctional(pkt);
    pkt->setAddr(orig_addr);
}

void
AddrMapper::recvFunctionalSnoop(PacketPtr pkt)
{
    Addr orig_addr = pkt->getAddr();
    pkt->setAddr(remapAddr(orig_addr));
    slavePort.sendFunctionalSnoop(pkt);
    pkt->setAddr(orig_addr);
}

Tick
AddrMapper::recvAtomic(PacketPtr pkt)
{
    Addr orig_addr = pkt->getAddr();
    pkt->setAddr(remapAddr(orig_addr));
    Tick ret_tick =  masterPort.sendAtomic(pkt);
    pkt->setAddr(orig_addr);
    return ret_tick;
}

Tick
AddrMapper::recvAtomicSnoop(PacketPtr pkt)
{
    Addr orig_addr = pkt->getAddr();
    pkt->setAddr(remapAddr(orig_addr));
    Tick ret_tick = slavePort.sendAtomicSnoop(pkt);
    pkt->setAddr(orig_addr);
    return ret_tick;
}

bool
AddrMapper::recvTimingReq(PacketPtr pkt)
{
    Addr orig_addr = pkt->getAddr();
    bool needsResponse = pkt->needsResponse();
    bool memInhibitAsserted = pkt->memInhibitAsserted();
    Packet::SenderState* senderState = pkt->senderState;

    if (needsResponse && !memInhibitAsserted) {
        pkt->senderState = new AddrMapperSenderState(senderState, orig_addr);
    }

    pkt->setAddr(remapAddr(orig_addr));

    // Attempt to send the packet (always succeeds for inhibited
    // packets)
    bool successful = masterPort.sendTimingReq(pkt);

    // If not successful, restore the sender state
    if (!successful && needsResponse) {
        delete pkt->senderState;
        pkt->senderState = senderState;
    }

    return successful;
}

bool
AddrMapper::recvTimingResp(PacketPtr pkt)
{
    AddrMapperSenderState* receivedState =
        dynamic_cast<AddrMapperSenderState*>(pkt->senderState);

    // Restore initial sender state
    if (receivedState == NULL)
        panic("AddrMapper %s got a response without sender state\n",
              name());

    Addr remapped_addr = pkt->getAddr();

    // Restore the state and address
    pkt->senderState = receivedState->origSenderState;
    pkt->setAddr(receivedState->origAddr);

    // Attempt to send the packet
    bool successful = slavePort.sendTimingResp(pkt);

    // If packet successfully sent, delete the sender state, otherwise
    // restore state
    if (successful) {
        delete receivedState;
    } else {
        // Don't delete anything and let the packet look like we did
        // not touch it
        pkt->senderState = receivedState;
        pkt->setAddr(remapped_addr);
    }
    return successful;
}

void
AddrMapper::recvTimingSnoopReq(PacketPtr pkt)
{
    slavePort.sendTimingSnoopReq(pkt);
}

bool
AddrMapper::recvTimingSnoopResp(PacketPtr pkt)
{
    return masterPort.sendTimingSnoopResp(pkt);
}

bool
AddrMapper::isSnooping() const
{
    if (slavePort.isSnooping())
        fatal("AddrMapper doesn't support remapping of snooping requests\n");
    return false;
}

unsigned
AddrMapper::deviceBlockSizeMaster()
{
    return slavePort.peerBlockSize();
}

unsigned
AddrMapper::deviceBlockSizeSlave()
{
    return masterPort.peerBlockSize();
}

void
AddrMapper::recvRetryMaster()
{
    slavePort.sendRetry();
}

void
AddrMapper::recvRetrySlave()
{
    masterPort.sendRetry();
}

void
AddrMapper::recvRangeChange()
{
    slavePort.sendRangeChange();
}

RangeAddrMapper::RangeAddrMapper(const RangeAddrMapperParams* p) :
    AddrMapper(p),
    originalRanges(p->original_ranges),
    remappedRanges(p->remapped_ranges)
{
    if (originalRanges.size() != remappedRanges.size())
        fatal("AddrMapper: original and shadowed range list must "
              "be same size\n");

    for (size_t x = 0; x < originalRanges.size(); x++) {
        if (originalRanges[x].size() != remappedRanges[x].size())
            fatal("AddrMapper: original and shadowed range list elements"
                  " aren't all of the same size\n");
    }
}

RangeAddrMapper*
RangeAddrMapperParams::create()
{
    return new RangeAddrMapper(this);
}

Addr
RangeAddrMapper::remapAddr(Addr addr) const
{
    for (int i = 0; i < originalRanges.size(); ++i) {
        if (originalRanges[i].contains(addr)) {
            Addr offset = addr - originalRanges[i].start();
            return offset + remappedRanges[i].start();
        }
    }

    return addr;
}

AddrRangeList
RangeAddrMapper::getAddrRanges() const
{
    // Simply return the original ranges as given by the parameters
    AddrRangeList ranges(originalRanges.begin(), originalRanges.end());
    return ranges;
}

<|endoftext|>