<commit_before>#include "src/associations.hpp"

#include "src/test/util/asio_server_class.hpp"
#include "src/test/util/association_objects.hpp"

#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>

#include <gtest/gtest.h>

class TestAssociations : public AsioServerClassTest
{
};
sdbusplus::asio::object_server* TestAssociations::AsioServerClassTest::server =
    nullptr;

// Verify call when path is not in associated owners
TEST_F(TestAssociations, SourcePathNotInAssociations)
{
    EXPECT_NE(nullptr, server);
    std::string sourcePath = "/xyz/openbmc_project/no/association";
    AssociationOwnersType assocOwners;
    AssociationInterfaces assocInterfaces;

    removeAssociation(sourcePath, DEFAULT_DBUS_SVC, *server, assocOwners,
                      assocInterfaces);
}

// Verify call when owner is not in associated owners
TEST_F(TestAssociations, OwnerNotInAssociations)
{
    AssociationInterfaces assocInterfaces;

    auto assocOwners = createDefaultOwnerAssociation();

    removeAssociation(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC, *server,
                      assocOwners, assocInterfaces);
}

// Verify call when path is not in associated interfaces
TEST_F(TestAssociations, PathNotInAssocInterfaces)
{
    AssociationInterfaces assocInterfaces;

    auto assocOwners = createDefaultOwnerAssociation();

    removeAssociation(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC, *server,
                      assocOwners, assocInterfaces);

    EXPECT_TRUE(assocOwners.empty());
}

// Verify call when path is in associated interfaces
TEST_F(TestAssociations, PathIsInAssociatedInterfaces)
{
    // Build up these objects so that an associated interface will match
    // with the associated owner being removed
    auto assocOwners = createDefaultOwnerAssociation();
    auto assocInterfaces = createDefaultInterfaceAssociation(server);

    removeAssociation(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC, *server,
                      assocOwners, assocInterfaces);

    // Verify owner association was deleted
    EXPECT_TRUE(assocOwners.empty());

    // Verify endpoint was deleted from interface association
    auto intfEndpoints =
        std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
    intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
}

// Verify call when path is in associated interfaces, with extra endpoints
TEST_F(TestAssociations, PathIsInAssociatedInterfacesExtraEndpoints)
{
    // Build up these objects so that an associated interface will match
    // with the associated owner being removed
    auto assocOwners = createDefaultOwnerAssociation();
    auto assocInterfaces = createDefaultInterfaceAssociation(server);

    // Add another endpoint to the assoc interfaces
    addEndpointToInterfaceAssociation(assocInterfaces);

    removeAssociation(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC, *server,
                      assocOwners, assocInterfaces);

    // Verify owner association was deleted
    EXPECT_TRUE(assocOwners.empty());

    // Verify all endpoints are deleted since source path was deleted
    auto intfEndpoints =
        std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
    intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
}

// Verify no associations or endpoints removed when the change is identical
TEST_F(TestAssociations, checkAssociationEndpointRemovesNoEpRemove)
{

    AssociationPaths newAssocPaths = {
        {DEFAULT_FWD_PATH, {DEFAULT_ENDPOINT}},
        {DEFAULT_REV_PATH, {DEFAULT_SOURCE_PATH}}};

    auto assocOwners = createDefaultOwnerAssociation();
    auto assocInterfaces = createDefaultInterfaceAssociation(server);

    checkAssociationEndpointRemoves(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC,
                                    newAssocPaths, *server, assocOwners,
                                    assocInterfaces);

    // Verify endpoints were not deleted because they matche with what was
    // in the original
    auto intfEndpoints =
        std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 1);
    intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 1);
}

// Verify endpoint is removed when assoc path is different
TEST_F(TestAssociations, checkAssociationEndpointRemovesEpRemoveApDiff)
{
    AssociationPaths newAssocPaths = {{"/different/path", {DEFAULT_ENDPOINT}}};

    auto assocOwners = createDefaultOwnerAssociation();
    auto assocInterfaces = createDefaultInterfaceAssociation(server);

    checkAssociationEndpointRemoves(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC,
                                    newAssocPaths, *server, assocOwners,
                                    assocInterfaces);

    // Verify initial endpoints were deleted because the new path
    auto intfEndpoints =
        std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
    intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
}

// Verify endpoint is removed when endpoint is different
TEST_F(TestAssociations, checkAssociationEndpointRemovesEpRemoveEpChanged)
{
    AssociationPaths newAssocPaths = {
        {DEFAULT_FWD_PATH, {DEFAULT_ENDPOINT + "/different"}},
        {DEFAULT_REV_PATH, {DEFAULT_SOURCE_PATH + "/different"}}};

    auto assocOwners = createDefaultOwnerAssociation();
    auto assocInterfaces = createDefaultInterfaceAssociation(server);

    checkAssociationEndpointRemoves(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC,
                                    newAssocPaths, *server, assocOwners,
                                    assocInterfaces);

    // Verify initial endpoints were deleted because of different endpoints
    auto intfEndpoints =
        std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
    intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
}
<commit_msg>unit-test: Test associationChanged()<commit_after>#include "src/associations.hpp"

#include "src/test/util/asio_server_class.hpp"
#include "src/test/util/association_objects.hpp"
#include "src/test/util/debug_output.hpp"

#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>

#include <gtest/gtest.h>

class TestAssociations : public AsioServerClassTest
{
};
sdbusplus::asio::object_server* TestAssociations::AsioServerClassTest::server =
    nullptr;

// Verify call when path is not in associated owners
TEST_F(TestAssociations, SourcePathNotInAssociations)
{
    EXPECT_NE(nullptr, server);
    std::string sourcePath = "/xyz/openbmc_project/no/association";
    AssociationOwnersType assocOwners;
    AssociationInterfaces assocInterfaces;

    removeAssociation(sourcePath, DEFAULT_DBUS_SVC, *server, assocOwners,
                      assocInterfaces);
}

// Verify call when owner is not in associated owners
TEST_F(TestAssociations, OwnerNotInAssociations)
{
    AssociationInterfaces assocInterfaces;

    auto assocOwners = createDefaultOwnerAssociation();

    removeAssociation(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC, *server,
                      assocOwners, assocInterfaces);
}

// Verify call when path is not in associated interfaces
TEST_F(TestAssociations, PathNotInAssocInterfaces)
{
    AssociationInterfaces assocInterfaces;

    auto assocOwners = createDefaultOwnerAssociation();

    removeAssociation(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC, *server,
                      assocOwners, assocInterfaces);

    EXPECT_TRUE(assocOwners.empty());
}

// Verify call when path is in associated interfaces
TEST_F(TestAssociations, PathIsInAssociatedInterfaces)
{
    // Build up these objects so that an associated interface will match
    // with the associated owner being removed
    auto assocOwners = createDefaultOwnerAssociation();
    auto assocInterfaces = createDefaultInterfaceAssociation(server);

    removeAssociation(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC, *server,
                      assocOwners, assocInterfaces);

    // Verify owner association was deleted
    EXPECT_TRUE(assocOwners.empty());

    // Verify endpoint was deleted from interface association
    auto intfEndpoints =
        std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
    intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
}

// Verify call when path is in associated interfaces, with extra endpoints
TEST_F(TestAssociations, PathIsInAssociatedInterfacesExtraEndpoints)
{
    // Build up these objects so that an associated interface will match
    // with the associated owner being removed
    auto assocOwners = createDefaultOwnerAssociation();
    auto assocInterfaces = createDefaultInterfaceAssociation(server);

    // Add another endpoint to the assoc interfaces
    addEndpointToInterfaceAssociation(assocInterfaces);

    removeAssociation(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC, *server,
                      assocOwners, assocInterfaces);

    // Verify owner association was deleted
    EXPECT_TRUE(assocOwners.empty());

    // Verify all endpoints are deleted since source path was deleted
    auto intfEndpoints =
        std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
    intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
}

// Verify no associations or endpoints removed when the change is identical
TEST_F(TestAssociations, checkAssociationEndpointRemovesNoEpRemove)
{

    AssociationPaths newAssocPaths = {
        {DEFAULT_FWD_PATH, {DEFAULT_ENDPOINT}},
        {DEFAULT_REV_PATH, {DEFAULT_SOURCE_PATH}}};

    auto assocOwners = createDefaultOwnerAssociation();
    auto assocInterfaces = createDefaultInterfaceAssociation(server);

    checkAssociationEndpointRemoves(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC,
                                    newAssocPaths, *server, assocOwners,
                                    assocInterfaces);

    // Verify endpoints were not deleted because they matche with what was
    // in the original
    auto intfEndpoints =
        std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 1);
    intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 1);
}

// Verify endpoint is removed when assoc path is different
TEST_F(TestAssociations, checkAssociationEndpointRemovesEpRemoveApDiff)
{
    AssociationPaths newAssocPaths = {{"/different/path", {DEFAULT_ENDPOINT}}};

    auto assocOwners = createDefaultOwnerAssociation();
    auto assocInterfaces = createDefaultInterfaceAssociation(server);

    checkAssociationEndpointRemoves(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC,
                                    newAssocPaths, *server, assocOwners,
                                    assocInterfaces);

    // Verify initial endpoints were deleted because the new path
    auto intfEndpoints =
        std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
    intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
}

// Verify endpoint is removed when endpoint is different
TEST_F(TestAssociations, checkAssociationEndpointRemovesEpRemoveEpChanged)
{
    AssociationPaths newAssocPaths = {
        {DEFAULT_FWD_PATH, {DEFAULT_ENDPOINT + "/different"}},
        {DEFAULT_REV_PATH, {DEFAULT_SOURCE_PATH + "/different"}}};

    auto assocOwners = createDefaultOwnerAssociation();
    auto assocInterfaces = createDefaultInterfaceAssociation(server);

    checkAssociationEndpointRemoves(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC,
                                    newAssocPaths, *server, assocOwners,
                                    assocInterfaces);

    // Verify initial endpoints were deleted because of different endpoints
    auto intfEndpoints =
        std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
    intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
}

// Verify existing endpoint deleted when empty endpoint is provided
TEST_F(TestAssociations, associationChangedEmptyEndpoint)
{
    std::vector<Association> associations = {{"inventory", "error", ""}};

    auto assocOwners = createDefaultOwnerAssociation();
    auto assocInterfaces = createDefaultInterfaceAssociation(server);

    // Empty endpoint will result in deletion of corresponding assocInterface
    associationChanged(*server, associations, DEFAULT_SOURCE_PATH,
                       DEFAULT_DBUS_SVC, assocOwners, assocInterfaces);

    // TODO - This test case found a bug where the endpoint validity
    // is not checked on the FWD path and is used by default, resulting
    // in there being a "" endpoint value. Will fix this in next commit
    // to keep the refactor of the code separate from the fix
    // (i.e. both of these should be 0 since we have an invalid endpoint)
    auto intfEndpoints =
        std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 1);
    intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);
}

// Add a new association with endpoint
TEST_F(TestAssociations, associationChangedAddNewAssoc)
{
    std::vector<Association> associations = {
        {"abc", "def", "/xyz/openbmc_project/new/endpoint"}};

    auto assocOwners = createDefaultOwnerAssociation();
    auto assocInterfaces = createDefaultInterfaceAssociation(server);

    associationChanged(*server, associations, "/new/source/path",
                       DEFAULT_DBUS_SVC, assocOwners, assocInterfaces);

    // Two source paths
    EXPECT_EQ(assocOwners.size(), 2);

    // Four interfaces
    EXPECT_EQ(assocInterfaces.size(), 4);

    // New endpoint so assocInterfaces should be same size
    auto intfEndpoints =
        std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 1);
}

// Add a new association to empty objects
TEST_F(TestAssociations, associationChangedAddNewAssocEmptyObj)
{
    std::string sourcePath = "/logging/entry/1";
    std::string owner = "xyz.openbmc_project.Test";
    std::vector<Association> associations = {
        {"inventory", "error",
         "/xyz/openbmc_project/inventory/system/chassis"}};

    // Empty objects because this test will ensure assocOwners adds the
    // changed association and interface
    AssociationInterfaces assocInterfaces;
    AssociationOwnersType assocOwners;

    associationChanged(*server, associations, DEFAULT_SOURCE_PATH,
                       DEFAULT_DBUS_SVC, assocOwners, assocInterfaces);

    // New associations so ensure it now contains a single entry
    EXPECT_EQ(assocOwners.size(), 1);

    // Verify corresponding assoc paths each have one endpoint in assoc
    // interfaces and that those endpoints match
    auto singleOwner = assocOwners[DEFAULT_SOURCE_PATH];
    auto singleIntf = singleOwner[DEFAULT_DBUS_SVC];
    for (auto i : singleIntf)
    {
        auto intfEndpoints = std::get<endpointsPos>(assocInterfaces[i.first]);
        EXPECT_EQ(intfEndpoints.size(), 1);
        EXPECT_EQ(intfEndpoints[0], *i.second.begin());
    }
}

// Add a new association to same source path but with new owner
TEST_F(TestAssociations, associationChangedAddNewAssocNewOwner)
{
    std::string newOwner = "xyz.openbmc_project.Test2";
    std::vector<Association> associations = {
        {"inventory", "error",
         "/xyz/openbmc_project/inventory/system/chassis"}};

    auto assocOwners = createDefaultOwnerAssociation();
    auto assocInterfaces = createDefaultInterfaceAssociation(server);

    associationChanged(*server, associations, DEFAULT_SOURCE_PATH, newOwner,
                       assocOwners, assocInterfaces);

    // New endpoint so assocOwners should be same size
    EXPECT_EQ(assocOwners.size(), 1);

    // Ensure only one endpoint under first path
    auto intfEndpoints =
        std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 1);

    // Ensure the 2 new association endpoints are under the new owner
    auto a = assocOwners.find(DEFAULT_SOURCE_PATH);
    auto o = a->second.find(newOwner);
    EXPECT_EQ(o->second.size(), 2);
}

// Add a new association to existing interface path
TEST_F(TestAssociations, associationChangedAddNewAssocSameInterface)
{
    std::vector<Association> associations = {
        {"abc", "error", "/xyz/openbmc_project/inventory/system/chassis"}};

    auto assocOwners = createDefaultOwnerAssociation();
    auto assocInterfaces = createDefaultInterfaceAssociation(server);

    associationChanged(*server, associations, DEFAULT_SOURCE_PATH,
                       DEFAULT_DBUS_SVC, assocOwners, assocInterfaces);

    // Should have 3 entries in AssociationInterfaces, one is just missing an
    // endpoint
    EXPECT_EQ(assocInterfaces.size(), 3);

    // Change to existing interface so it will be removed here
    auto intfEndpoints =
        std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
    EXPECT_EQ(intfEndpoints.size(), 0);

    // The new endpoint should exist though in it's place
    intfEndpoints = std::get<endpointsPos>(
        assocInterfaces[DEFAULT_SOURCE_PATH + "/" + "abc"]);
    EXPECT_EQ(intfEndpoints.size(), 1);

    // Added to an existing owner path so still 1
    EXPECT_EQ(assocOwners.size(), 1);
}
<|endoftext|>


<commit_before>
// ================================================================================================
// -*- C++ -*-
// File: filesys.cpp
// Author: Guilherme R. Lampert
// Created on: 12/12/14
// Brief: Some File-System and file handling helpers.
//
// This project's source code is released under the MIT License.
// - http://opensource.org/licenses/MIT
//
// ================================================================================================

#include "utils/filesys.hpp"

// POSIX includes:
#include <sys/types.h>
#include <sys/stat.h>
#ifndef WIN32
    #include <unistd.h>
#else
    #include <direct.h> // _mkdir
#endif
#include <errno.h>

#if defined(WIN32) || defined(WIN64)
    // Copied from linux libc sys/stat.h:
    #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
    #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif

namespace utils
{
namespace filesys
{

// ========================================================
// getPathSeparator():
// ========================================================

const char * getPathSeparator() noexcept
{
	return "/";
}

// ========================================================
// removeFilenameExtension():
// ========================================================

std::string removeFilenameExtension(const std::string & filename)
{
	const size_t lastDot = filename.find_last_of('.');
	if (lastDot == std::string::npos)
	{
		return filename;
	}
	return filename.substr(0, lastDot);
}

// ========================================================
// getFilenameExtension():
// ========================================================

std::string getFilenameExtension(const std::string & filename, const bool includeDot)
{
	std::string extension;
	const size_t lastDot = filename.find_last_of('.');
	if (lastDot != std::string::npos)
	{
		const long extensionChars = filename.length() - lastDot;
		if (extensionChars > 0)
		{
			extension.assign(filename, includeDot ? lastDot : (lastDot + 1), extensionChars);
		}
	}
	return extension;
}

// ========================================================
// queryFileSize():
// ========================================================

bool queryFileSize(const std::string & filename, size_t & sizeInBytes)
{
	assert(!filename.empty());

	errno = 0;
	struct stat statBuf = {};
	if (stat(filename.c_str(), &statBuf) == 0 && S_ISREG(statBuf.st_mode))
	{
		sizeInBytes = static_cast<size_t>(statBuf.st_size);
		return true;
	}

	sizeInBytes = 0;
	return false;
}

// ========================================================
// createDirectory():
// ========================================================

bool createDirectory(const std::string & dirPath)
{
	assert(!dirPath.empty());

	errno = 0;
	struct stat dirStat = {};
	if (stat(dirPath.c_str(), &dirStat) != 0)
	{
#ifndef WIN32
        if (_mkdir(dirPath.c_str(), 0777) != 0)
#else
        if (_mkdir(dirPath.c_str()) != 0)
#endif
		{
			return false;
		}
	}
	else // Path already exists:
	{
		if (!S_ISDIR(dirStat.st_mode))
		{
			// Looks like there is a file with the same name
			// as the directory we are trying to create!
			return false;
		}
	}

	return true;
}

// ========================================================
// createPath():
// ========================================================

bool createPath(const std::string & pathEndedWithSeparatorOrFilename)
{
	char dirPath[MaxTempStringLen];

	assert(!pathEndedWithSeparatorOrFilename.empty());
	assert(pathEndedWithSeparatorOrFilename.length() < arrayLength(dirPath) && "Pathname too long!");

	std::strncpy(dirPath, pathEndedWithSeparatorOrFilename.c_str(), arrayLength(dirPath) - 1);
	char * pPath = dirPath;

	while (*pPath != '\0')
	{
		// Works for both Win and Unix without the need for extra tweaks.
		if (*pPath == '/' || *pPath == '\\')
		{
			*pPath = '\0';
			if (!createDirectory(dirPath))
			{
				return false;
			}
			*pPath = *getPathSeparator();
		}
		++pPath;
	}

	return true;
}

// ========================================================
// tryOpen() for ofstream and ifstream:
// ========================================================

bool tryOpen(std::ofstream & file, const std::string & filename, const std::ofstream::open_mode mode)
{
	assert(!filename.empty());
	assert(!file.is_open()); // Close it before calling this!

	errno = 0;
	file.exceptions(0); // Don't throw on error.
	file.open(filename, mode | std::ofstream::out);

	return file.is_open() && file.good();
}

bool tryOpen(std::ifstream & file, const std::string & filename, const std::ifstream::open_mode mode)
{
	assert(!filename.empty());
	assert(!file.is_open()); // Close it before calling this!

	errno = 0;
	file.exceptions(0); // Don't throw on error.
	file.open(filename, mode | std::ifstream::in);

	return file.is_open() && file.good();
}

// ========================================================
// getLastFileError():
// ========================================================

std::string getLastFileError()
{
	std::string str{ std::strerror(errno) };
	errno = 0;
	return str;
}

} // namespace filesys {}
} // namespace utils {}
<commit_msg>Cleaned up the mkdir Unix/Windows mess with a helper macro.<commit_after>
// ================================================================================================
// -*- C++ -*-
// File: filesys.cpp
// Author: Guilherme R. Lampert
// Created on: 12/12/14
// Brief: Some File-System and file handling helpers.
//
// This project's source code is released under the MIT License.
// - http://opensource.org/licenses/MIT
//
// ================================================================================================

#include "utils/filesys.hpp"

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

#if defined(WIN32) || defined(WIN64)
    #include <direct.h> // _mkdir
    // Copied from linux libc sys/stat.h:
    #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
    #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
    #define SIEGE_MAKE_DIR(dirname) _mkdir(dirname)
#else // !WINDOWS
    #include <unistd.h>
    #define SIEGE_MAKE_DIR(dirname) mkdir((dirname), 0777)
#endif // WINDOWS

namespace utils
{
namespace filesys
{

// ========================================================
// getPathSeparator():
// ========================================================

const char * getPathSeparator() noexcept
{
	return "/";
}

// ========================================================
// removeFilenameExtension():
// ========================================================

std::string removeFilenameExtension(const std::string & filename)
{
	const size_t lastDot = filename.find_last_of('.');
	if (lastDot == std::string::npos)
	{
		return filename;
	}
	return filename.substr(0, lastDot);
}

// ========================================================
// getFilenameExtension():
// ========================================================

std::string getFilenameExtension(const std::string & filename, const bool includeDot)
{
	std::string extension;
	const size_t lastDot = filename.find_last_of('.');
	if (lastDot != std::string::npos)
	{
		const long extensionChars = filename.length() - lastDot;
		if (extensionChars > 0)
		{
			extension.assign(filename, includeDot ? lastDot : (lastDot + 1), extensionChars);
		}
	}
	return extension;
}

// ========================================================
// queryFileSize():
// ========================================================

bool queryFileSize(const std::string & filename, size_t & sizeInBytes)
{
	assert(!filename.empty());

	errno = 0;
	struct stat statBuf = {};
	if (stat(filename.c_str(), &statBuf) == 0 && S_ISREG(statBuf.st_mode))
	{
		sizeInBytes = static_cast<size_t>(statBuf.st_size);
		return true;
	}

	sizeInBytes = 0;
	return false;
}

// ========================================================
// createDirectory():
// ========================================================

bool createDirectory(const std::string & dirPath)
{
	assert(!dirPath.empty());

	errno = 0;
	struct stat dirStat = {};
	if (stat(dirPath.c_str(), &dirStat) != 0)
	{
        if (SIEGE_MAKE_DIR(dirPath.c_str()) != 0)
		{
			return false;
		}
	}
	else // Path already exists:
	{
		if (!S_ISDIR(dirStat.st_mode))
		{
			// Looks like there is a file with the same name
			// as the directory we are trying to create!
			return false;
		}
	}

	return true;
}

// ========================================================
// createPath():
// ========================================================

bool createPath(const std::string & pathEndedWithSeparatorOrFilename)
{
	char dirPath[MaxTempStringLen];

	assert(!pathEndedWithSeparatorOrFilename.empty());
	assert(pathEndedWithSeparatorOrFilename.length() < arrayLength(dirPath) && "Pathname too long!");

	std::strncpy(dirPath, pathEndedWithSeparatorOrFilename.c_str(), arrayLength(dirPath) - 1);
	char * pPath = dirPath;

	while (*pPath != '\0')
	{
		// Works for both Win and Unix without the need for extra tweaks.
		if (*pPath == '/' || *pPath == '\\')
		{
			*pPath = '\0';
			if (!createDirectory(dirPath))
			{
				return false;
			}
			*pPath = *getPathSeparator();
		}
		++pPath;
	}

	return true;
}

// ========================================================
// tryOpen() for ofstream and ifstream:
// ========================================================

bool tryOpen(std::ofstream & file, const std::string & filename, const std::ofstream::open_mode mode)
{
	assert(!filename.empty());
	assert(!file.is_open()); // Close it before calling this!

	errno = 0;
	file.exceptions(0); // Don't throw on error.
	file.open(filename, mode | std::ofstream::out);

	return file.is_open() && file.good();
}

bool tryOpen(std::ifstream & file, const std::string & filename, const std::ifstream::open_mode mode)
{
	assert(!filename.empty());
	assert(!file.is_open()); // Close it before calling this!

	errno = 0;
	file.exceptions(0); // Don't throw on error.
	file.open(filename, mode | std::ifstream::in);

	return file.is_open() && file.good();
}

// ========================================================
// getLastFileError():
// ========================================================

std::string getLastFileError()
{
	std::string str{ std::strerror(errno) };
	errno = 0;
	return str;
}

} // namespace filesys {}
} // namespace utils {}
<|endoftext|>


<commit_before>// Copyright (c) 2011-2015 The Bitcoin Core developers
// Copyright (c) 2015-2019 The Bitcoin Unlimited developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#define BOOST_TEST_MODULE Bitcoin Test Suite

#include "test_bitcoin.h"

#include "chainparams.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "crypto/sha256.h"
#include "fs.h"
#include "key.h"
#include "main.h"
#include "miner.h"
#include "parallel.h"
#include "pubkey.h"
#include "random.h"
#include "rpc/register.h"
#include "rpc/server.h"
#include "script/sigcache.h"
#include "test/testutil.h"
#include "txadmission.h"
#include "txdb.h"
#include "txmempool.h"
#include "ui_interface.h"
#include "validation/validation.h"

#include <memory>

#include <boost/program_options.hpp>
#include <boost/test/unit_test.hpp>
#include <thread>

FastRandomContext insecure_rand_ctx(true);

extern bool fPrintToConsole;
extern void noui_connect();

BasicTestingSetup::BasicTestingSetup(const std::string &chainName)
{
    // Do not place the data created by these unit tests on top of any existing chain,
    // by overriding datadir to use a temporary if it isn't already overridden
    if (mapArgs.count("-datadir") == 0)
        mapArgs["-datadir"] = GetTempPath().string();
    SHA256AutoDetect();
    ECC_Start();
    SetupEnvironment();
    SetupNetworking();
    InitSignatureCache();
    fPrintToDebugLog = false; // don't want to write to debug.log file
    fCheckBlockIndex = true;
    SelectParams(chainName);
    noui_connect();
}

BasicTestingSetup::~BasicTestingSetup() { ECC_Stop(); }
TestingSetup::TestingSetup(const std::string &chainName) : BasicTestingSetup(chainName)
{
    const CChainParams &chainparams = Params();
    // Ideally we'd move all the RPC tests to the functional testing framework
    // instead of unit tests, but for now we need these here.
    RegisterAllCoreRPCCommands(tableRPC);
    ClearDatadirCache();
    pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(1 << 30)));
    fs::create_directories(pathTemp);
    pblocktree = new CBlockTreeDB(1 << 20, "", true);
    pcoinsdbview = new CCoinsViewDB(1 << 23, true);
    pcoinsTip = new CCoinsViewCache(pcoinsdbview);
    txCommitQ = new std::map<uint256, CTxCommitData>();
    bool worked = InitBlockIndex(chainparams);
    assert(worked);

    // Initial dbcache settings so that the automatic cache setting don't kick in and allow
    // us to accidentally use up our RAM on Travis, and also so that we are not prevented from flushing the
    // dbcache if the need arises in the unit tests (dbcache must be less than the DEFAULT_HIGH_PERF_MEM_CUTOFF
    // to allow all cache entries to be flushed).
    SoftSetArg("-dbcache", std::to_string(5));
    nCoinCacheMaxSize.store(5000000);

    // Make sure there are 3 script check threads running for each queue
    SoftSetArg("-par", std::to_string(3));
    PV.reset(new CParallelValidation());

    RegisterNodeSignals(GetNodeSignals());
}

TestingSetup::~TestingSetup()
{
    UnregisterNodeSignals(GetNodeSignals());
    threadGroup.interrupt_all();
    threadGroup.join_all();
    UnloadBlockIndex();
    delete pcoinsTip;
    delete pcoinsdbview;
    delete pblocktree;
    fs::remove_all(pathTemp);
}

struct NumericallyLessTxHashComparator
{
public:
    bool operator()(const CTransactionRef &a, const CTransactionRef &b) const { return a->GetHash() < b->GetHash(); }
};

TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
{
    // Generate a 100-block chain:
    coinbaseKey.MakeNewKey(true);
    CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
    for (int i = 0; i < COINBASE_MATURITY; i++)
    {
        std::vector<CMutableTransaction> noTxns;
        CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
        coinbaseTxns.push_back(*b.vtx[0]);
    }
}

//
// Create a new block with just given transactions, coinbase paying to
// scriptPubKey, and try to add it to the current chain.
//
CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction> &txns,
    const CScript &scriptPubKey)
{
    const CChainParams &chainparams = Params();
    std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
    pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
    CBlock &block = pblocktemplate->block;

    // Replace mempool-selected txns with just coinbase plus passed-in txns:
    block.vtx.resize(1);
    for (const CMutableTransaction &tx : txns)
        block.vtx.push_back(MakeTransactionRef(tx));

    // enfore LTOR ordering of transactions
    std::sort(block.vtx.begin() + 1, block.vtx.end(), NumericallyLessTxHashComparator());

    // IncrementExtraNonce creates a valid coinbase and merkleRoot
    unsigned int extraNonce = 0;
    IncrementExtraNonce(&block, extraNonce);

    while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus()))
        ++block.nNonce;

    CValidationState state;
    ProcessNewBlock(state, chainparams, nullptr, &block, true, nullptr, false);

    CBlock result = block;
    return result;
}

TestChain100Setup::~TestChain100Setup() {}
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx, CTxMemPool *pool)
{
    CTransaction txn(tx);
    return FromTx(txn, pool);
}

CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn, CTxMemPool *pool)
{
    bool hasNoDependencies = pool ? pool->HasNoInputsOf(txn) : hadNoDependencies;
    // Hack to assume either its completely dependent on other mempool txs or not at all
    CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0;

    CTxMemPoolEntry ret(MakeTransactionRef(txn), nFee, nTime, dPriority, nHeight, hasNoDependencies, inChainValue,
        spendsCoinbase, sigOpCount, lp);
    ret.sighashType = SIGHASH_ALL; // For testing, give the transaction any valid sighashtype
    return ret;
}

void Shutdown(void *parg) { exit(0); }
void StartShutdown() { exit(0); }
bool ShutdownRequested() { return false; }
using namespace boost::program_options;

CService ipaddress(uint32_t i, uint32_t port)
{
    struct in_addr s;
    s.s_addr = i;
    return CService(CNetAddr(s), port);
}

struct StartupShutdown
{
    StartupShutdown()
    {
        options_description optDef("Options");
        optDef.add_options()("testhelp", "program options information")(
            "log_level", "set boost logging (all, test_suite, message, warning, error, ...)")(
            "log_bitcoin", value<std::string>()->required(), "bitcoin logging destination (console, none)");
        variables_map opts;
        store(parse_command_line(boost::unit_test::framework::master_test_suite().argc,
                  boost::unit_test::framework::master_test_suite().argv, optDef),
            opts);

        if (opts.count("testhelp"))
        {
            std::cout << optDef << std::endl;
            exit(0);
        }

        if (opts.count("log_bitcoin"))
        {
            std::string s = opts["log_bitcoin"].as<std::string>();
            if (s == "console")
            {
                /* To enable this, add
                   -- --log_bitcoin console
                   to the end of the test_bitcoin argument list. */
                Logging::LogToggleCategory(ALL, true);
                fPrintToConsole = true;
                fPrintToDebugLog = false;
            }
            else if (s == "none")
            {
                fPrintToConsole = false;
                fPrintToDebugLog = false;
            }
        }
    }
    ~StartupShutdown() { UnlimitedCleanup(); }
};

#if BOOST_VERSION >= 106500
BOOST_TEST_GLOBAL_FIXTURE(StartupShutdown);
#else
BOOST_GLOBAL_FIXTURE(StartupShutdown);
#endif

std::ostream &operator<<(std::ostream &os, const uint256 &num)
{
    os << num.ToString();
    return os;
}
<commit_msg>Re-enable -limitfreerealy in our unit tests<commit_after>// Copyright (c) 2011-2015 The Bitcoin Core developers
// Copyright (c) 2015-2019 The Bitcoin Unlimited developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#define BOOST_TEST_MODULE Bitcoin Test Suite

#include "test_bitcoin.h"

#include "chainparams.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "crypto/sha256.h"
#include "fs.h"
#include "key.h"
#include "main.h"
#include "miner.h"
#include "parallel.h"
#include "pubkey.h"
#include "random.h"
#include "rpc/register.h"
#include "rpc/server.h"
#include "script/sigcache.h"
#include "test/testutil.h"
#include "txadmission.h"
#include "txdb.h"
#include "txmempool.h"
#include "ui_interface.h"
#include "validation/validation.h"

#include <memory>

#include <boost/program_options.hpp>
#include <boost/test/unit_test.hpp>
#include <thread>

FastRandomContext insecure_rand_ctx(true);

extern bool fPrintToConsole;
extern void noui_connect();

BasicTestingSetup::BasicTestingSetup(const std::string &chainName)
{
    // Do not place the data created by these unit tests on top of any existing chain,
    // by overriding datadir to use a temporary if it isn't already overridden
    if (mapArgs.count("-datadir") == 0)
        mapArgs["-datadir"] = GetTempPath().string();
    SHA256AutoDetect();
    ECC_Start();
    SetupEnvironment();
    SetupNetworking();
    InitSignatureCache();
    fPrintToDebugLog = false; // don't want to write to debug.log file
    fCheckBlockIndex = true;
    SelectParams(chainName);
    noui_connect();
}

BasicTestingSetup::~BasicTestingSetup() { ECC_Stop(); }
TestingSetup::TestingSetup(const std::string &chainName) : BasicTestingSetup(chainName)
{
    const CChainParams &chainparams = Params();
    // Ideally we'd move all the RPC tests to the functional testing framework
    // instead of unit tests, but for now we need these here.
    RegisterAllCoreRPCCommands(tableRPC);
    ClearDatadirCache();
    pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(1 << 30)));
    fs::create_directories(pathTemp);
    pblocktree = new CBlockTreeDB(1 << 20, "", true);
    pcoinsdbview = new CCoinsViewDB(1 << 23, true);
    pcoinsTip = new CCoinsViewCache(pcoinsdbview);
    txCommitQ = new std::map<uint256, CTxCommitData>();
    bool worked = InitBlockIndex(chainparams);
    assert(worked);

    // -limitfreerelay is diabled by default but some tests rely on it so make sure to set it here.
    SetArg("-limitfreerelay", std::to_string(15));

    // Initial dbcache settings so that the automatic cache setting don't kick in and allow
    // us to accidentally use up our RAM on Travis, and also so that we are not prevented from flushing the
    // dbcache if the need arises in the unit tests (dbcache must be less than the DEFAULT_HIGH_PERF_MEM_CUTOFF
    // to allow all cache entries to be flushed).
    SoftSetArg("-dbcache", std::to_string(5));
    nCoinCacheMaxSize.store(5000000);

    // Make sure there are 3 script check threads running for each queue
    SoftSetArg("-par", std::to_string(3));
    PV.reset(new CParallelValidation());

    RegisterNodeSignals(GetNodeSignals());
}

TestingSetup::~TestingSetup()
{
    UnregisterNodeSignals(GetNodeSignals());
    threadGroup.interrupt_all();
    threadGroup.join_all();
    UnloadBlockIndex();
    delete pcoinsTip;
    delete pcoinsdbview;
    delete pblocktree;
    fs::remove_all(pathTemp);
}

struct NumericallyLessTxHashComparator
{
public:
    bool operator()(const CTransactionRef &a, const CTransactionRef &b) const { return a->GetHash() < b->GetHash(); }
};

TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
{
    // Generate a 100-block chain:
    coinbaseKey.MakeNewKey(true);
    CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
    for (int i = 0; i < COINBASE_MATURITY; i++)
    {
        std::vector<CMutableTransaction> noTxns;
        CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
        coinbaseTxns.push_back(*b.vtx[0]);
    }
}

//
// Create a new block with just given transactions, coinbase paying to
// scriptPubKey, and try to add it to the current chain.
//
CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction> &txns,
    const CScript &scriptPubKey)
{
    const CChainParams &chainparams = Params();
    std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
    pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
    CBlock &block = pblocktemplate->block;

    // Replace mempool-selected txns with just coinbase plus passed-in txns:
    block.vtx.resize(1);
    for (const CMutableTransaction &tx : txns)
        block.vtx.push_back(MakeTransactionRef(tx));

    // enfore LTOR ordering of transactions
    std::sort(block.vtx.begin() + 1, block.vtx.end(), NumericallyLessTxHashComparator());

    // IncrementExtraNonce creates a valid coinbase and merkleRoot
    unsigned int extraNonce = 0;
    IncrementExtraNonce(&block, extraNonce);

    while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus()))
        ++block.nNonce;

    CValidationState state;
    ProcessNewBlock(state, chainparams, nullptr, &block, true, nullptr, false);

    CBlock result = block;
    return result;
}

TestChain100Setup::~TestChain100Setup() {}
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx, CTxMemPool *pool)
{
    CTransaction txn(tx);
    return FromTx(txn, pool);
}

CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn, CTxMemPool *pool)
{
    bool hasNoDependencies = pool ? pool->HasNoInputsOf(txn) : hadNoDependencies;
    // Hack to assume either its completely dependent on other mempool txs or not at all
    CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0;

    CTxMemPoolEntry ret(MakeTransactionRef(txn), nFee, nTime, dPriority, nHeight, hasNoDependencies, inChainValue,
        spendsCoinbase, sigOpCount, lp);
    ret.sighashType = SIGHASH_ALL; // For testing, give the transaction any valid sighashtype
    return ret;
}

void Shutdown(void *parg) { exit(0); }
void StartShutdown() { exit(0); }
bool ShutdownRequested() { return false; }
using namespace boost::program_options;

CService ipaddress(uint32_t i, uint32_t port)
{
    struct in_addr s;
    s.s_addr = i;
    return CService(CNetAddr(s), port);
}

struct StartupShutdown
{
    StartupShutdown()
    {
        options_description optDef("Options");
        optDef.add_options()("testhelp", "program options information")(
            "log_level", "set boost logging (all, test_suite, message, warning, error, ...)")(
            "log_bitcoin", value<std::string>()->required(), "bitcoin logging destination (console, none)");
        variables_map opts;
        store(parse_command_line(boost::unit_test::framework::master_test_suite().argc,
                  boost::unit_test::framework::master_test_suite().argv, optDef),
            opts);

        if (opts.count("testhelp"))
        {
            std::cout << optDef << std::endl;
            exit(0);
        }

        if (opts.count("log_bitcoin"))
        {
            std::string s = opts["log_bitcoin"].as<std::string>();
            if (s == "console")
            {
                /* To enable this, add
                   -- --log_bitcoin console
                   to the end of the test_bitcoin argument list. */
                Logging::LogToggleCategory(ALL, true);
                fPrintToConsole = true;
                fPrintToDebugLog = false;
            }
            else if (s == "none")
            {
                fPrintToConsole = false;
                fPrintToDebugLog = false;
            }
        }
    }
    ~StartupShutdown() { UnlimitedCleanup(); }
};

#if BOOST_VERSION >= 106500
BOOST_TEST_GLOBAL_FIXTURE(StartupShutdown);
#else
BOOST_GLOBAL_FIXTURE(StartupShutdown);
#endif

std::ostream &operator<<(std::ostream &os, const uint256 &num)
{
    os << num.ToString();
    return os;
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Soonho Kong
*/
#include <vector>
#include <algorithm>
#include "format.h"
#include "test.h"
#include "sexpr_funcs.h"

using namespace lean;

using std::cout;
using std::endl;

static void tst1() {
    format f_atom1("foo");
    format f_atom2("bar");
    format f_atom3(1);
    format f_atom4(3.1415);
    format f1(highlight(f_atom1), f_atom2);
//    format f2{f_atom1, f_atom2, highlight(f_atom3, format::format_color::ORANGE), f_atom4};
    format f2{f_atom1, highlight(f_atom3, format::format_color::ORANGE)};
    format f3 = compose(f1, f2);
    format f4 = nest(3, f3);
    format f5 = line();
    format f6(f4, f5);
    format f7 = nest(10, format{f6, f4, f6, f4});
//    format f8(f_atom1, nest(3, format(line(), f_atom1)));
    format f8 = f7;
    format f9 = f7 + f8;
    format f10;
    f10 += f6 + f5 + f3;
    format f11 = above(f1, above(above(f2, f3), f4));
    format f12 = paren(format{format("a"),
                              format("b"),
                              format("c"),
                              format("d")});

    std::vector<std::pair<std::string, format> > v =
        {{"f1",  f1},
         {"f2",  f2},
         {"f3",  f3},
         {"f4",  f4},
         {"f5",  f5},
         {"f6",  f6},
         {"f7",  f7},
         {"f8",  f8},
         {"f9",  f9},
         {"f10", f10},
         {"f11", f11},
         {"f12", f12},
        };

    std::for_each(v.begin(), v.end(),
                  [](std::pair<std::string, format> const & p) {
                      cout << "---- " << p.first << " ----------" << endl
                                << p.second << endl
                                << "--------------------" << endl << endl;
                  });

    std::vector<std::string> ss = {"Last", "weekend's", "revelation", "that", "J.K.", "Rowling", "is", "the", "author", "of", "the", "critically", "acclaimed", "and", "--", "until", "now", "--", "commercially", "unsuccessful", "crime", "novel", "The", "Cuckoo's", "Calling", "has", "electrified", "the", "book", "world", "and", "solidified", "Rowling's", "reputation", "as", "a", "genuine", "writing", "talent:", "After", "all,", "if", "she", "can", "impress", "the", "critics", "without", "the", "benefit", "of", "her", "towering", "reputation,", "then", "surely", "her", "success", "is", "deserved."};

    cout << accumulate(++ss.begin(),
               ss.end(),
               format(*ss.begin()),
               [](format const & result, std::string const & s) {
                   return wrap(result, format(s));
               });

    std::vector<format> sl = {f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12};

    cout << "fill" << endl;
    cout << std::string(40, '=') << endl;
    pretty(cout, 60, fill(sl.begin(), sl.end()));
    cout << endl;
    cout << std::string(40, '=') << endl;

    cout << "stack" << endl;
    cout << std::string(40, '=') << endl;
    pretty(cout, 60, stack(sl.begin(), sl.end()));
    cout << endl;
    cout << std::string(40, '=') << endl;

    cout << "spread" << endl;
    cout << std::string(40, '=') << endl;
    pretty(cout, 60, spread(sl.begin(), sl.end()));
    cout << endl;
    cout << std::string(40, '=') << endl;

//    cout << fill(ss.begin(), ss.end()) << endl;
    return;
}

static void tst2() {
    format f4 = nest(3, compose(format("foo"), compose(line(), format("bla"))));
    cout << f4 << "\n";
    cout << paren(f4) << "\n";
}

int main() {
    continue_on_violation(true);
    tst1();
    tst2();
    return has_violations() ? 1 : 0;
}
<commit_msg>Update format test<commit_after>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Soonho Kong
*/
#include <vector>
#include <algorithm>
#include "format.h"
#include "test.h"
#include "sexpr_funcs.h"

using namespace lean;

using std::cout;
using std::endl;

static void tst1() {
    format f_atom1("foo");
    format f_atom2("bar");
    format f_atom3(1);
    format f_atom4(3.1415);
    format f1(highlight(f_atom1), f_atom2);
    format f2{f_atom1, f_atom2, highlight(f_atom3, format::format_color::ORANGE), f_atom4};
    format f3 = compose(f1, f2);
    format f4 = nest(3, f3);
    format f5 = line();
    format f6(f4, f5);
    format f7 = nest(10, format{f6, f4, f6, f4});
    format f8(f_atom1, nest(3, format(line(), f_atom1)));
    format f9 = f7 + f8;
    format f10;
    f10 += f6 + f5 + f3;
    format f11 = above(f1, above(above(f2, f3), f4));
    format f12 = paren(format{format("a"),
                              format("b"),
                              format("c"),
                              format("d")});

    std::vector<std::pair<std::string, format> > v =
        {{"f1",  f1},
         {"f2",  f2},
         {"f3",  f3},
         {"f4",  f4},
         {"f5",  f5},
         {"f6",  f6},
         {"f7",  f7},
         {"f8",  f8},
         {"f9",  f9},
         {"f10", f10},
         {"f11", f11},
         {"f12", f12},
        };

    std::for_each(v.begin(), v.end(),
                  [](std::pair<std::string, format> const & p) {
                      cout << "---- " << p.first << " ----------" << endl
                           << p.second << endl
                           << "--------------------" << endl << endl;
                  });

    std::vector<std::string> ss = {"Last", "weekend's", "revelation", "that", "J.K.", "Rowling", "is", "the", "author", "of", "the", "critically", "acclaimed", "and", "--", "until", "now", "--", "commercially", "unsuccessful", "crime", "novel", "The", "Cuckoo's", "Calling", "has", "electrified", "the", "book", "world", "and", "solidified", "Rowling's", "reputation", "as", "a", "genuine", "writing", "talent:", "After", "all,", "if", "she", "can", "impress", "the", "critics", "without", "the", "benefit", "of", "her", "towering", "reputation,", "then", "surely", "her", "success", "is", "deserved."};

    cout << fillwords(ss.begin(), ss.end());

    std::vector<format> sl = {f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12};

    cout << "fill" << endl;
    cout << std::string(40, '=') << endl;
    pretty(cout, 60, fill(sl.begin(), sl.end()));
    cout << endl;
    cout << std::string(40, '=') << endl;

    cout << "stack" << endl;
    cout << std::string(40, '=') << endl;
    pretty(cout, 60, stack(sl.begin(), sl.end()));
    cout << endl;
    cout << std::string(40, '=') << endl;

    cout << "spread" << endl;
    cout << std::string(40, '=') << endl;
    pretty(cout, 60, spread(sl.begin(), sl.end()));
    cout << endl;
    cout << std::string(40, '=') << endl;

    return;
}

static void tst2() {
    format f4 = nest(3, compose(format("foo"), compose(line(), format("bla"))));
    cout << f4 << "\n";
    cout << paren(f4) << "\n";
}

int main() {
    continue_on_violation(true);
    tst1();
    tst2();
    return has_violations() ? 1 : 0;
}
<|endoftext|>


<commit_before>/*-
 * Copyright 2011-2012 Matthew Endsley
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted providing that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 */

#include "TINYAUDIO/tinyaudio.h"

#include <stdint.h>
#include <stdio.h>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>

namespace tinyaudio {

struct AndroidPlayer {
	samples_callback callback;
	int currentBuffer;

	static const int c_nbuffers = 2;
	static const int c_nsamples = 2048;
	int16_t buffers[c_nbuffers][c_nsamples * 2];
};

static AndroidPlayer g_player = {0};

static const int c_nlasterror = 128;
static char g_lasterror[c_nlasterror];

static void SLAPIENTRY audio_callback(SLAndroidSimpleBufferQueueItf bq, void* context) {

	AndroidPlayer* p = (AndroidPlayer*)context;

	int16_t* buffer = p->buffers[p->currentBuffer];

	p->callback(buffer, p->c_nsamples);
	(*bq)->Enqueue(bq, buffer, sizeof(int16_t) * 2 * p->c_nsamples);

	p->currentBuffer = (p->currentBuffer + 1 ) % p->c_nbuffers;
}

bool init(int sample_rate, samples_callback callback) {

	g_lasterror[0] = 0;
	g_player.callback = callback;

	static const SLEngineOption engineOpts[] = {
		(SLuint32) SL_ENGINEOPTION_THREADSAFE, (SLuint32) SL_BOOLEAN_FALSE,
	};

	SLObjectItf iface;
	SLresult res = slCreateEngine(&iface, 1, engineOpts, 0, NULL, NULL);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to create opensl engine: %d", res);
		return false;
	}

	res = (*iface)->Realize(iface, SL_BOOLEAN_FALSE);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to realize engine: %d", res);
		return false;
	}

	SLEngineItf engine;
	res = (*iface)->GetInterface(iface, SL_IID_ENGINE, &engine);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to get engine interface: %d", res);
		return false;
	}

	SLObjectItf outputmix;
	res = (*engine)->CreateOutputMix(engine, &outputmix, 0, NULL, NULL);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to create output mix: %d", res);
		return false;
	}

	res = (*outputmix)->Realize(outputmix, SL_BOOLEAN_FALSE);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to realize output mix: %d", res);
		return false;
	}

	SLDataLocator_AndroidSimpleBufferQueue bufferQueueDesc = {
		SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
		2,
	};
	SLDataFormat_PCM format = {
		SL_DATAFORMAT_PCM,
		2,
		SL_SAMPLINGRATE_44_1,
		SL_PCMSAMPLEFORMAT_FIXED_16,
		SL_PCMSAMPLEFORMAT_FIXED_16,
		SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
		SL_BYTEORDER_LITTLEENDIAN,
	};
	SLDataSource source = {
		&bufferQueueDesc,
		&format,
	};

	SLDataLocator_OutputMix output = {
		SL_DATALOCATOR_OUTPUTMIX,
		outputmix,
	};
	SLDataSink sink = {
		&output,
		NULL,
	};

	static const SLInterfaceID playerIfaces[] = {
		SL_IID_BUFFERQUEUE,
	};
	static const SLboolean playerIfaceReqs[] = {
		SL_BOOLEAN_TRUE,
	};
	SLObjectItf player;
	res = (*engine)->CreateAudioPlayer(engine, &player, &source, &sink, 1, playerIfaces, playerIfaceReqs);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to create audio player: %d", res);
		return false;
	}

	res = (*player)->Realize(player, SL_BOOLEAN_FALSE);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to realize audio player: %d", res);
		return false;
	}

	SLPlayItf play;
	res = (*player)->GetInterface(player, SL_IID_PLAY, &play);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to get SL_IID_PLAY interface: %d", res);
		return false;
	}

	SLAndroidSimpleBufferQueueItf bufferQueue;
	res = (*player)->GetInterface(player, SL_IID_BUFFERQUEUE, &bufferQueue);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to get the SL_IID_BUFFERQUEUE interface: %d", res);
		return false;
	}

	res = (*bufferQueue)->RegisterCallback(bufferQueue, audio_callback, &g_player);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to register bufferqueue callback: %d", res);
		return false;
	}

	res = (*play)->SetPlayState(play, SL_PLAYSTATE_PLAYING);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to start playing SL_IID_PLAY interface: %d", res);
		return false;
	}

	for (int ii = 0; ii < g_player.c_nbuffers; ++ii) {
		audio_callback(bufferQueue, &g_player);
	}

	return true;
}

void release() {
}

const char* last_error() {
	return g_lasterror;
}

}
<commit_msg>don't ignore incoming sample_rate on android<commit_after>/*-
 * Copyright 2011-2012 Matthew Endsley
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted providing that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 */

#include "TINYAUDIO/tinyaudio.h"

#include <stdint.h>
#include <stdio.h>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>

namespace tinyaudio {

struct AndroidPlayer {
	samples_callback callback;
	int currentBuffer;

	static const int c_nbuffers = 2;
	static const int c_nsamples = 2048;
	int16_t buffers[c_nbuffers][c_nsamples * 2];
};

static AndroidPlayer g_player = {0};

static const int c_nlasterror = 128;
static char g_lasterror[c_nlasterror];

static void SLAPIENTRY audio_callback(SLAndroidSimpleBufferQueueItf bq, void* context) {

	AndroidPlayer* p = (AndroidPlayer*)context;

	int16_t* buffer = p->buffers[p->currentBuffer];

	p->callback(buffer, p->c_nsamples);
	(*bq)->Enqueue(bq, buffer, sizeof(int16_t) * 2 * p->c_nsamples);

	p->currentBuffer = (p->currentBuffer + 1 ) % p->c_nbuffers;
}

bool init(int sample_rate, samples_callback callback) {

	g_lasterror[0] = 0;
	g_player.callback = callback;

	SLmilliHertz samplerate;
	switch (sample_rate) {
	case 8000: samplerate = SL_SAMPLINGRATE_8; break;
	case 11025: samplerate = SL_SAMPLINGRATE_11_025; break;
	case 12000: samplerate = SL_SAMPLINGRATE_12; break;
	case 16000: samplerate = SL_SAMPLINGRATE_16; break;
	case 22050: samplerate = SL_SAMPLINGRATE_22_05; break;
	case 24000: samplerate = SL_SAMPLINGRATE_24; break;
	case 32000: samplerate = SL_SAMPLINGRATE_32; break;
	case 44100: samplerate = SL_SAMPLINGRATE_44_1; break;
	case 48000: samplerate = SL_SAMPLINGRATE_48; break;
	case 64000: samplerate = SL_SAMPLINGRATE_64; break;
	case 88200: samplerate = SL_SAMPLINGRATE_88_2; break;
	case 96000: samplerate = SL_SAMPLINGRATE_96; break;
	case 192000: samplerate = SL_SAMPLINGRATE_192; break;
	default:
		snprintf(g_lasterror, c_nlasterror, "Unsupported sample rate %d", sample_rate);
		return false;
	}

	static const SLEngineOption engineOpts[] = {
		(SLuint32) SL_ENGINEOPTION_THREADSAFE, (SLuint32) SL_BOOLEAN_FALSE,
	};

	SLObjectItf iface;
	SLresult res = slCreateEngine(&iface, 1, engineOpts, 0, NULL, NULL);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to create opensl engine: %d", res);
		return false;
	}

	res = (*iface)->Realize(iface, SL_BOOLEAN_FALSE);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to realize engine: %d", res);
		return false;
	}

	SLEngineItf engine;
	res = (*iface)->GetInterface(iface, SL_IID_ENGINE, &engine);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to get engine interface: %d", res);
		return false;
	}

	SLObjectItf outputmix;
	res = (*engine)->CreateOutputMix(engine, &outputmix, 0, NULL, NULL);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to create output mix: %d", res);
		return false;
	}

	res = (*outputmix)->Realize(outputmix, SL_BOOLEAN_FALSE);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to realize output mix: %d", res);
		return false;
	}

	SLDataLocator_AndroidSimpleBufferQueue bufferQueueDesc = {
		SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
		2,
	};
	SLDataFormat_PCM format = {
		SL_DATAFORMAT_PCM,
		2,
		samplerate,
		SL_PCMSAMPLEFORMAT_FIXED_16,
		SL_PCMSAMPLEFORMAT_FIXED_16,
		SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
		SL_BYTEORDER_LITTLEENDIAN,
	};
	SLDataSource source = {
		&bufferQueueDesc,
		&format,
	};

	SLDataLocator_OutputMix output = {
		SL_DATALOCATOR_OUTPUTMIX,
		outputmix,
	};
	SLDataSink sink = {
		&output,
		NULL,
	};

	static const SLInterfaceID playerIfaces[] = {
		SL_IID_BUFFERQUEUE,
	};
	static const SLboolean playerIfaceReqs[] = {
		SL_BOOLEAN_TRUE,
	};
	SLObjectItf player;
	res = (*engine)->CreateAudioPlayer(engine, &player, &source, &sink, 1, playerIfaces, playerIfaceReqs);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to create audio player: %d", res);
		return false;
	}

	res = (*player)->Realize(player, SL_BOOLEAN_FALSE);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to realize audio player: %d", res);
		return false;
	}

	SLPlayItf play;
	res = (*player)->GetInterface(player, SL_IID_PLAY, &play);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to get SL_IID_PLAY interface: %d", res);
		return false;
	}

	SLAndroidSimpleBufferQueueItf bufferQueue;
	res = (*player)->GetInterface(player, SL_IID_BUFFERQUEUE, &bufferQueue);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to get the SL_IID_BUFFERQUEUE interface: %d", res);
		return false;
	}

	res = (*bufferQueue)->RegisterCallback(bufferQueue, audio_callback, &g_player);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to register bufferqueue callback: %d", res);
		return false;
	}

	res = (*play)->SetPlayState(play, SL_PLAYSTATE_PLAYING);
	if (res != SL_RESULT_SUCCESS) {
		snprintf(g_lasterror, c_nlasterror, "Failed to start playing SL_IID_PLAY interface: %d", res);
		return false;
	}

	for (int ii = 0; ii < g_player.c_nbuffers; ++ii) {
		audio_callback(bufferQueue, &g_player);
	}

	return true;
}

void release() {
}

const char* last_error() {
	return g_lasterror;
}

}
<|endoftext|>


<commit_before>//===-- DYLDRendezvous.cpp --------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// C Includes
// C++ Includes
// Other libraries and framework includes
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"

#include "llvm/Support/Path.h"

#include "DYLDRendezvous.h"

using namespace lldb;
using namespace lldb_private;

/// Locates the address of the rendezvous structure.  Returns the address on
/// success and LLDB_INVALID_ADDRESS on failure.
static addr_t
ResolveRendezvousAddress(Process *process)
{
    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
    addr_t info_location;
    addr_t info_addr;
    Error error;

    if (!process)
    {
        if (log)
            log->Printf ("%s null process provided", __FUNCTION__);
        return LLDB_INVALID_ADDRESS;
    }

    // Try to get it from our process.  This might be a remote process and might
    // grab it via some remote-specific mechanism.
    info_location = process->GetImageInfoAddress();
    if (log)
        log->Printf ("%s info_location = 0x%" PRIx64, __FUNCTION__, info_location);

    // If the process fails to return an address, fall back to seeing if the local object file can help us find it.
    if (info_location == LLDB_INVALID_ADDRESS)
    {
        Target *target = &process->GetTarget();
        if (target)
        {
            ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile();
            Address addr = obj_file->GetImageInfoAddress(target);

            if (addr.IsValid())
            {
                info_location = addr.GetLoadAddress(target);
                if (log)
                    log->Printf ("%s resolved via direct object file approach to 0x%" PRIx64, __FUNCTION__, info_location);
            }
            else
            {
                if (log)
                    log->Printf ("%s FAILED - direct object file approach did not yield a valid address", __FUNCTION__);
            }
        }
    }

    if (info_location == LLDB_INVALID_ADDRESS)
    {
        if (log)
            log->Printf ("%s FAILED - invalid info address", __FUNCTION__);
        return LLDB_INVALID_ADDRESS;
    }

    if (log)
        log->Printf ("%s reading pointer (%" PRIu32 " bytes) from 0x%" PRIx64, __FUNCTION__, process->GetAddressByteSize(), info_location);

    info_addr = process->ReadPointerFromMemory(info_location, error);
    if (error.Fail())
    {
        if (log)
            log->Printf ("%s FAILED - could not read from the info location: %s", __FUNCTION__, error.AsCString ());
        return LLDB_INVALID_ADDRESS;
    }

    if (info_addr == 0)
    {
        if (log)
            log->Printf ("%s FAILED - the rendezvous address contained at 0x%" PRIx64 " returned a null value", __FUNCTION__, info_location);
        return LLDB_INVALID_ADDRESS;
    }

    return info_addr;
}

DYLDRendezvous::DYLDRendezvous(Process *process)
    : m_process(process),
      m_rendezvous_addr(LLDB_INVALID_ADDRESS),
      m_current(),
      m_previous(),
      m_soentries(),
      m_added_soentries(),
      m_removed_soentries()
{
    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));

    m_thread_info.valid = false;

    // Cache a copy of the executable path
    if (m_process)
    {
        Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer();
        if (exe_mod)
        {
            exe_mod->GetPlatformFileSpec().GetPath(m_exe_path, PATH_MAX);
            if (log)
                log->Printf ("DYLDRendezvous::%s exe module executable path set: '%s'", __FUNCTION__, m_exe_path);
        }
        else
        {
            if (log)
                log->Printf ("DYLDRendezvous::%s cannot cache exe module path: null executable module pointer", __FUNCTION__);
        }
    }
}

bool
DYLDRendezvous::Resolve()
{
    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));

    const size_t word_size = 4;
    Rendezvous info;
    size_t address_size;
    size_t padding;
    addr_t info_addr;
    addr_t cursor;

    address_size = m_process->GetAddressByteSize();
    padding = address_size - word_size;
    if (log)
        log->Printf ("DYLDRendezvous::%s address size: %zu, padding %zu", __FUNCTION__, address_size, padding);

    if (m_rendezvous_addr == LLDB_INVALID_ADDRESS)
        cursor = info_addr = ResolveRendezvousAddress(m_process);
    else
        cursor = info_addr = m_rendezvous_addr;
    if (log)
        log->Printf ("DYLDRendezvous::%s cursor = 0x%" PRIx64, __FUNCTION__, cursor);

    if (cursor == LLDB_INVALID_ADDRESS)
        return false;

    if (!(cursor = ReadWord(cursor, &info.version, word_size)))
        return false;

    if (!(cursor = ReadPointer(cursor + padding, &info.map_addr)))
        return false;

    if (!(cursor = ReadPointer(cursor, &info.brk)))
        return false;

    if (!(cursor = ReadWord(cursor, &info.state, word_size)))
        return false;

    if (!(cursor = ReadPointer(cursor + padding, &info.ldbase)))
        return false;

    // The rendezvous was successfully read.  Update our internal state.
    m_rendezvous_addr = info_addr;
    m_previous = m_current;
    m_current = info;

    return UpdateSOEntries();
}

bool
DYLDRendezvous::IsValid()
{
    return m_rendezvous_addr != LLDB_INVALID_ADDRESS;
}

bool
DYLDRendezvous::UpdateSOEntries()
{
    SOEntry entry;

    if (m_current.map_addr == 0)
        return false;

    // When the previous and current states are consistent this is the first
    // time we have been asked to update.  Just take a snapshot of the currently
    // loaded modules.
    if (m_previous.state == eConsistent && m_current.state == eConsistent)
        return TakeSnapshot(m_soentries);

    // If we are about to add or remove a shared object clear out the current
    // state and take a snapshot of the currently loaded images.
    if (m_current.state == eAdd || m_current.state == eDelete)
    {
        assert(m_previous.state == eConsistent || (m_previous.state == eAdd && m_current.state == eDelete));
        m_soentries.clear();
        m_added_soentries.clear();
        m_removed_soentries.clear();
        return TakeSnapshot(m_soentries);
    }
    assert(m_current.state == eConsistent);

    // Otherwise check the previous state to determine what to expect and update
    // accordingly.
    if (m_previous.state == eAdd)
        return UpdateSOEntriesForAddition();
    else if (m_previous.state == eDelete)
        return UpdateSOEntriesForDeletion();

    return false;
}

bool
DYLDRendezvous::UpdateSOEntriesForAddition()
{
    SOEntry entry;
    iterator pos;

    assert(m_previous.state == eAdd);

    if (m_current.map_addr == 0)
        return false;

    for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next)
    {
        if (!ReadSOEntryFromMemory(cursor, entry))
            return false;

        // Only add shared libraries and not the executable.
        if (SOEntryIsMainExecutable(entry))
            continue;

        pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
        if (pos == m_soentries.end())
        {
            m_soentries.push_back(entry);
            m_added_soentries.push_back(entry);
        }
    }

    return true;
}

bool
DYLDRendezvous::UpdateSOEntriesForDeletion()
{
    SOEntryList entry_list;
    iterator pos;

    assert(m_previous.state == eDelete);

    if (!TakeSnapshot(entry_list))
        return false;

    for (iterator I = begin(); I != end(); ++I)
    {
        pos = std::find(entry_list.begin(), entry_list.end(), *I);
        if (pos == entry_list.end())
            m_removed_soentries.push_back(*I);
    }

    m_soentries = entry_list;
    return true;
}

bool
DYLDRendezvous::SOEntryIsMainExecutable(const SOEntry &entry)
{
    // On Linux the executable is indicated by an empty path in the entry. On
    // FreeBSD and on Android it is the full path to the executable.

    auto triple = m_process->GetTarget().GetArchitecture().GetTriple();
    auto os_type = triple.getOS();
    auto env_type = triple.getEnvironment();

    switch (os_type) {
        case llvm::Triple::FreeBSD:
            return ::strcmp(entry.path.c_str(), m_exe_path) == 0;
        case llvm::Triple::Linux:
            switch (env_type) {
                case llvm::Triple::Android:
                    return ::strcmp(entry.path.c_str(), m_exe_path) == 0;
                default:
                    return entry.path.empty();
            }
        default:
            return false;
    }
}

bool
DYLDRendezvous::TakeSnapshot(SOEntryList &entry_list)
{
    SOEntry entry;

    if (m_current.map_addr == 0)
        return false;

    for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next)
    {
        if (!ReadSOEntryFromMemory(cursor, entry))
            return false;

        // Only add shared libraries and not the executable.
        if (SOEntryIsMainExecutable(entry))
            continue;

        entry_list.push_back(entry);
    }

    return true;
}

addr_t
DYLDRendezvous::ReadWord(addr_t addr, uint64_t *dst, size_t size)
{
    Error error;

    *dst = m_process->ReadUnsignedIntegerFromMemory(addr, size, 0, error);
    if (error.Fail())
        return 0;

    return addr + size;
}

addr_t
DYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst)
{
    Error error;

    *dst = m_process->ReadPointerFromMemory(addr, error);
    if (error.Fail())
        return 0;

    return addr + m_process->GetAddressByteSize();
}

std::string
DYLDRendezvous::ReadStringFromMemory(addr_t addr)
{
    std::string str;
    Error error;

    if (addr == LLDB_INVALID_ADDRESS)
        return std::string();

    m_process->ReadCStringFromMemory(addr, str, error);

    return str;
}

bool
DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry)
{
    entry.clear();

    entry.link_addr = addr;

    if (!(addr = ReadPointer(addr, &entry.base_addr)))
        return false;

    // mips adds an extra load offset field to the link map struct on
    // FreeBSD and NetBSD (need to validate other OSes).
    // http://svnweb.freebsd.org/base/head/sys/sys/link_elf.h?revision=217153&view=markup#l57
    const ArchSpec &arch = m_process->GetTarget().GetArchitecture();
    if (arch.GetCore() == ArchSpec::eCore_mips64)
    {
        assert (arch.GetTriple().getOS() == llvm::Triple::FreeBSD ||
                arch.GetTriple().getOS() == llvm::Triple::NetBSD);
        addr_t mips_l_offs;
        if (!(addr = ReadPointer(addr, &mips_l_offs)))
            return false;
        if (mips_l_offs != 0 && mips_l_offs != entry.base_addr)
            return false;
    }

    if (!(addr = ReadPointer(addr, &entry.path_addr)))
        return false;

    if (!(addr = ReadPointer(addr, &entry.dyn_addr)))
        return false;

    if (!(addr = ReadPointer(addr, &entry.next)))
        return false;

    if (!(addr = ReadPointer(addr, &entry.prev)))
        return false;

    entry.path = ReadStringFromMemory(entry.path_addr);

    return true;
}

bool
DYLDRendezvous::FindMetadata(const char *name, PThreadField field, uint32_t& value)
{
    Target& target = m_process->GetTarget();

    SymbolContextList list;
    if (!target.GetImages().FindSymbolsWithNameAndType (ConstString(name), eSymbolTypeAny, list))
        return false;

    Address address = list[0].symbol->GetAddress();
    addr_t addr = address.GetLoadAddress (&target);
    if (addr == LLDB_INVALID_ADDRESS)
        return false;

    Error error;
    value = (uint32_t)m_process->ReadUnsignedIntegerFromMemory(addr + field*sizeof(uint32_t), sizeof(uint32_t), 0, error);
    if (error.Fail())
        return false;

    if (field == eSize)
        value /= 8; // convert bits to bytes

    return true;
}

const DYLDRendezvous::ThreadInfo&
DYLDRendezvous::GetThreadInfo()
{
    if (!m_thread_info.valid)
    {
        bool ok = true;

        ok &= FindMetadata ("_thread_db_pthread_dtvp", eOffset, m_thread_info.dtv_offset);
        ok &= FindMetadata ("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size);
        ok &= FindMetadata ("_thread_db_link_map_l_tls_modid", eOffset, m_thread_info.modid_offset);
        ok &= FindMetadata ("_thread_db_dtv_t_pointer_val", eOffset, m_thread_info.tls_offset);

        if (ok)
            m_thread_info.valid = true;
    }

    return m_thread_info;
}

void
DYLDRendezvous::DumpToLog(Log *log) const
{
    int state = GetState();

    if (!log)
        return;

    log->PutCString("DYLDRendezvous:");
    log->Printf("   Address: %" PRIx64, GetRendezvousAddress());
    log->Printf("   Version: %" PRIu64, GetVersion());
    log->Printf("   Link   : %" PRIx64, GetLinkMapAddress());
    log->Printf("   Break  : %" PRIx64, GetBreakAddress());
    log->Printf("   LDBase : %" PRIx64, GetLDBase());
    log->Printf("   State  : %s",
                (state == eConsistent) ? "consistent" :
                (state == eAdd)        ? "add"        :
                (state == eDelete)     ? "delete"     : "unknown");

    iterator I = begin();
    iterator E = end();

    if (I != E)
        log->PutCString("DYLDRendezvous SOEntries:");

    for (int i = 1; I != E; ++I, ++i)
    {
        log->Printf("\n   SOEntry [%d] %s", i, I->path.c_str());
        log->Printf("      Base : %" PRIx64, I->base_addr);
        log->Printf("      Path : %" PRIx64, I->path_addr);
        log->Printf("      Dyn  : %" PRIx64, I->dyn_addr);
        log->Printf("      Next : %" PRIx64, I->next);
        log->Printf("      Prev : %" PRIx64, I->prev);
    }
}
<commit_msg>Do not assert on POSIXDYLD double-eAdd.<commit_after>//===-- DYLDRendezvous.cpp --------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// C Includes
// C++ Includes
// Other libraries and framework includes
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"

#include "llvm/Support/Path.h"

#include "DYLDRendezvous.h"

using namespace lldb;
using namespace lldb_private;

/// Locates the address of the rendezvous structure.  Returns the address on
/// success and LLDB_INVALID_ADDRESS on failure.
static addr_t
ResolveRendezvousAddress(Process *process)
{
    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
    addr_t info_location;
    addr_t info_addr;
    Error error;

    if (!process)
    {
        if (log)
            log->Printf ("%s null process provided", __FUNCTION__);
        return LLDB_INVALID_ADDRESS;
    }

    // Try to get it from our process.  This might be a remote process and might
    // grab it via some remote-specific mechanism.
    info_location = process->GetImageInfoAddress();
    if (log)
        log->Printf ("%s info_location = 0x%" PRIx64, __FUNCTION__, info_location);

    // If the process fails to return an address, fall back to seeing if the local object file can help us find it.
    if (info_location == LLDB_INVALID_ADDRESS)
    {
        Target *target = &process->GetTarget();
        if (target)
        {
            ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile();
            Address addr = obj_file->GetImageInfoAddress(target);

            if (addr.IsValid())
            {
                info_location = addr.GetLoadAddress(target);
                if (log)
                    log->Printf ("%s resolved via direct object file approach to 0x%" PRIx64, __FUNCTION__, info_location);
            }
            else
            {
                if (log)
                    log->Printf ("%s FAILED - direct object file approach did not yield a valid address", __FUNCTION__);
            }
        }
    }

    if (info_location == LLDB_INVALID_ADDRESS)
    {
        if (log)
            log->Printf ("%s FAILED - invalid info address", __FUNCTION__);
        return LLDB_INVALID_ADDRESS;
    }

    if (log)
        log->Printf ("%s reading pointer (%" PRIu32 " bytes) from 0x%" PRIx64, __FUNCTION__, process->GetAddressByteSize(), info_location);

    info_addr = process->ReadPointerFromMemory(info_location, error);
    if (error.Fail())
    {
        if (log)
            log->Printf ("%s FAILED - could not read from the info location: %s", __FUNCTION__, error.AsCString ());
        return LLDB_INVALID_ADDRESS;
    }

    if (info_addr == 0)
    {
        if (log)
            log->Printf ("%s FAILED - the rendezvous address contained at 0x%" PRIx64 " returned a null value", __FUNCTION__, info_location);
        return LLDB_INVALID_ADDRESS;
    }

    return info_addr;
}

DYLDRendezvous::DYLDRendezvous(Process *process)
    : m_process(process),
      m_rendezvous_addr(LLDB_INVALID_ADDRESS),
      m_current(),
      m_previous(),
      m_soentries(),
      m_added_soentries(),
      m_removed_soentries()
{
    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));

    m_thread_info.valid = false;

    // Cache a copy of the executable path
    if (m_process)
    {
        Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer();
        if (exe_mod)
        {
            exe_mod->GetPlatformFileSpec().GetPath(m_exe_path, PATH_MAX);
            if (log)
                log->Printf ("DYLDRendezvous::%s exe module executable path set: '%s'", __FUNCTION__, m_exe_path);
        }
        else
        {
            if (log)
                log->Printf ("DYLDRendezvous::%s cannot cache exe module path: null executable module pointer", __FUNCTION__);
        }
    }
}

bool
DYLDRendezvous::Resolve()
{
    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));

    const size_t word_size = 4;
    Rendezvous info;
    size_t address_size;
    size_t padding;
    addr_t info_addr;
    addr_t cursor;

    address_size = m_process->GetAddressByteSize();
    padding = address_size - word_size;
    if (log)
        log->Printf ("DYLDRendezvous::%s address size: %zu, padding %zu", __FUNCTION__, address_size, padding);

    if (m_rendezvous_addr == LLDB_INVALID_ADDRESS)
        cursor = info_addr = ResolveRendezvousAddress(m_process);
    else
        cursor = info_addr = m_rendezvous_addr;
    if (log)
        log->Printf ("DYLDRendezvous::%s cursor = 0x%" PRIx64, __FUNCTION__, cursor);

    if (cursor == LLDB_INVALID_ADDRESS)
        return false;

    if (!(cursor = ReadWord(cursor, &info.version, word_size)))
        return false;

    if (!(cursor = ReadPointer(cursor + padding, &info.map_addr)))
        return false;

    if (!(cursor = ReadPointer(cursor, &info.brk)))
        return false;

    if (!(cursor = ReadWord(cursor, &info.state, word_size)))
        return false;

    if (!(cursor = ReadPointer(cursor + padding, &info.ldbase)))
        return false;

    // The rendezvous was successfully read.  Update our internal state.
    m_rendezvous_addr = info_addr;
    m_previous = m_current;
    m_current = info;

    return UpdateSOEntries();
}

bool
DYLDRendezvous::IsValid()
{
    return m_rendezvous_addr != LLDB_INVALID_ADDRESS;
}

bool
DYLDRendezvous::UpdateSOEntries()
{
    SOEntry entry;

    if (m_current.map_addr == 0)
        return false;

    // When the previous and current states are consistent this is the first
    // time we have been asked to update.  Just take a snapshot of the currently
    // loaded modules.
    if (m_previous.state == eConsistent && m_current.state == eConsistent)
        return TakeSnapshot(m_soentries);

    // If we are about to add or remove a shared object clear out the current
    // state and take a snapshot of the currently loaded images.
    if (m_current.state == eAdd || m_current.state == eDelete)
    {
        // Some versions of the android dynamic linker might send two
        // notifications with state == eAdd back to back. Ignore them
        // until we get an eConsistent notification.
        if (!(m_previous.state == eConsistent || (m_previous.state == eAdd && m_current.state == eDelete)))
            return false;

        m_soentries.clear();
        m_added_soentries.clear();
        m_removed_soentries.clear();
        return TakeSnapshot(m_soentries);
    }
    assert(m_current.state == eConsistent);

    // Otherwise check the previous state to determine what to expect and update
    // accordingly.
    if (m_previous.state == eAdd)
        return UpdateSOEntriesForAddition();
    else if (m_previous.state == eDelete)
        return UpdateSOEntriesForDeletion();

    return false;
}

bool
DYLDRendezvous::UpdateSOEntriesForAddition()
{
    SOEntry entry;
    iterator pos;

    assert(m_previous.state == eAdd);

    if (m_current.map_addr == 0)
        return false;

    for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next)
    {
        if (!ReadSOEntryFromMemory(cursor, entry))
            return false;

        // Only add shared libraries and not the executable.
        if (SOEntryIsMainExecutable(entry))
            continue;

        pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
        if (pos == m_soentries.end())
        {
            m_soentries.push_back(entry);
            m_added_soentries.push_back(entry);
        }
    }

    return true;
}

bool
DYLDRendezvous::UpdateSOEntriesForDeletion()
{
    SOEntryList entry_list;
    iterator pos;

    assert(m_previous.state == eDelete);

    if (!TakeSnapshot(entry_list))
        return false;

    for (iterator I = begin(); I != end(); ++I)
    {
        pos = std::find(entry_list.begin(), entry_list.end(), *I);
        if (pos == entry_list.end())
            m_removed_soentries.push_back(*I);
    }

    m_soentries = entry_list;
    return true;
}

bool
DYLDRendezvous::SOEntryIsMainExecutable(const SOEntry &entry)
{
    // On Linux the executable is indicated by an empty path in the entry. On
    // FreeBSD and on Android it is the full path to the executable.

    auto triple = m_process->GetTarget().GetArchitecture().GetTriple();
    auto os_type = triple.getOS();
    auto env_type = triple.getEnvironment();

    switch (os_type) {
        case llvm::Triple::FreeBSD:
            return ::strcmp(entry.path.c_str(), m_exe_path) == 0;
        case llvm::Triple::Linux:
            switch (env_type) {
                case llvm::Triple::Android:
                    return ::strcmp(entry.path.c_str(), m_exe_path) == 0;
                default:
                    return entry.path.empty();
            }
        default:
            return false;
    }
}

bool
DYLDRendezvous::TakeSnapshot(SOEntryList &entry_list)
{
    SOEntry entry;

    if (m_current.map_addr == 0)
        return false;

    for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next)
    {
        if (!ReadSOEntryFromMemory(cursor, entry))
            return false;

        // Only add shared libraries and not the executable.
        if (SOEntryIsMainExecutable(entry))
            continue;

        entry_list.push_back(entry);
    }

    return true;
}

addr_t
DYLDRendezvous::ReadWord(addr_t addr, uint64_t *dst, size_t size)
{
    Error error;

    *dst = m_process->ReadUnsignedIntegerFromMemory(addr, size, 0, error);
    if (error.Fail())
        return 0;

    return addr + size;
}

addr_t
DYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst)
{
    Error error;

    *dst = m_process->ReadPointerFromMemory(addr, error);
    if (error.Fail())
        return 0;

    return addr + m_process->GetAddressByteSize();
}

std::string
DYLDRendezvous::ReadStringFromMemory(addr_t addr)
{
    std::string str;
    Error error;

    if (addr == LLDB_INVALID_ADDRESS)
        return std::string();

    m_process->ReadCStringFromMemory(addr, str, error);

    return str;
}

bool
DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry)
{
    entry.clear();

    entry.link_addr = addr;

    if (!(addr = ReadPointer(addr, &entry.base_addr)))
        return false;

    // mips adds an extra load offset field to the link map struct on
    // FreeBSD and NetBSD (need to validate other OSes).
    // http://svnweb.freebsd.org/base/head/sys/sys/link_elf.h?revision=217153&view=markup#l57
    const ArchSpec &arch = m_process->GetTarget().GetArchitecture();
    if (arch.GetCore() == ArchSpec::eCore_mips64)
    {
        assert (arch.GetTriple().getOS() == llvm::Triple::FreeBSD ||
                arch.GetTriple().getOS() == llvm::Triple::NetBSD);
        addr_t mips_l_offs;
        if (!(addr = ReadPointer(addr, &mips_l_offs)))
            return false;
        if (mips_l_offs != 0 && mips_l_offs != entry.base_addr)
            return false;
    }

    if (!(addr = ReadPointer(addr, &entry.path_addr)))
        return false;

    if (!(addr = ReadPointer(addr, &entry.dyn_addr)))
        return false;

    if (!(addr = ReadPointer(addr, &entry.next)))
        return false;

    if (!(addr = ReadPointer(addr, &entry.prev)))
        return false;

    entry.path = ReadStringFromMemory(entry.path_addr);

    return true;
}

bool
DYLDRendezvous::FindMetadata(const char *name, PThreadField field, uint32_t& value)
{
    Target& target = m_process->GetTarget();

    SymbolContextList list;
    if (!target.GetImages().FindSymbolsWithNameAndType (ConstString(name), eSymbolTypeAny, list))
        return false;

    Address address = list[0].symbol->GetAddress();
    addr_t addr = address.GetLoadAddress (&target);
    if (addr == LLDB_INVALID_ADDRESS)
        return false;

    Error error;
    value = (uint32_t)m_process->ReadUnsignedIntegerFromMemory(addr + field*sizeof(uint32_t), sizeof(uint32_t), 0, error);
    if (error.Fail())
        return false;

    if (field == eSize)
        value /= 8; // convert bits to bytes

    return true;
}

const DYLDRendezvous::ThreadInfo&
DYLDRendezvous::GetThreadInfo()
{
    if (!m_thread_info.valid)
    {
        bool ok = true;

        ok &= FindMetadata ("_thread_db_pthread_dtvp", eOffset, m_thread_info.dtv_offset);
        ok &= FindMetadata ("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size);
        ok &= FindMetadata ("_thread_db_link_map_l_tls_modid", eOffset, m_thread_info.modid_offset);
        ok &= FindMetadata ("_thread_db_dtv_t_pointer_val", eOffset, m_thread_info.tls_offset);

        if (ok)
            m_thread_info.valid = true;
    }

    return m_thread_info;
}

void
DYLDRendezvous::DumpToLog(Log *log) const
{
    int state = GetState();

    if (!log)
        return;

    log->PutCString("DYLDRendezvous:");
    log->Printf("   Address: %" PRIx64, GetRendezvousAddress());
    log->Printf("   Version: %" PRIu64, GetVersion());
    log->Printf("   Link   : %" PRIx64, GetLinkMapAddress());
    log->Printf("   Break  : %" PRIx64, GetBreakAddress());
    log->Printf("   LDBase : %" PRIx64, GetLDBase());
    log->Printf("   State  : %s",
                (state == eConsistent) ? "consistent" :
                (state == eAdd)        ? "add"        :
                (state == eDelete)     ? "delete"     : "unknown");

    iterator I = begin();
    iterator E = end();

    if (I != E)
        log->PutCString("DYLDRendezvous SOEntries:");

    for (int i = 1; I != E; ++I, ++i)
    {
        log->Printf("\n   SOEntry [%d] %s", i, I->path.c_str());
        log->Printf("      Base : %" PRIx64, I->base_addr);
        log->Printf("      Path : %" PRIx64, I->path_addr);
        log->Printf("      Dyn  : %" PRIx64, I->dyn_addr);
        log->Printf("      Next : %" PRIx64, I->next);
        log->Printf("      Prev : %" PRIx64, I->prev);
    }
}
<|endoftext|>