Compare commits

...

8 Commits

Author SHA1 Message Date
Dino Santl
7d305b66f9 Add formated test 2018-12-18 12:35:29 +01:00
Dino Santl
04275ce3c0 Add test and small string escape fix 2018-12-17 11:25:39 +01:00
Dino Santl
83c5222abd Adjust code to documentation 2018-12-14 00:51:34 +01:00
Dino Santl
7c06b2584f Add error handling and list support 2018-12-11 18:38:52 +01:00
Dino Santl
9fe8169435 Add comments to code 2018-12-07 12:25:17 +01:00
Dino Santl
bc1d5cf72c Rename tensorflow op and add input types and input parameter list 2018-12-06 16:55:00 +01:00
Dino Santl
d19a61c519 Add beter compile option for tf lib 2018-12-04 14:31:21 +01:00
Dino Santl
e5bffeaf67 Add tensorflow op 2018-12-03 13:32:18 +01:00
13 changed files with 575 additions and 17 deletions

View File

@@ -99,7 +99,8 @@ import_external_library(fmt STATIC
${CMAKE_CURRENT_SOURCE_DIR}/fmt/lib/libfmt.a
${CMAKE_CURRENT_SOURCE_DIR}/fmt/include
# Skip testing.
CMAKE_ARGS -DFMT_TEST=OFF)
CMAKE_ARGS -DFMT_TEST=OFF
-DCMAKE_POSITION_INDEPENDENT_CODE=ON)
# Setup ltalloc library
@@ -147,7 +148,8 @@ import_external_library(gflags STATIC
# Don't register installation in ~/.cmake
CMAKE_ARGS -DREGISTER_INSTALL_PREFIX=OFF
-DBUILD_gflags_nothreads_LIB=OFF
-DGFLAGS_NO_FILENAMES=${GFLAGS_NO_FILENAMES})
-DGFLAGS_NO_FILENAMES=${GFLAGS_NO_FILENAMES}
-DCMAKE_POSITION_INDEPENDENT_CODE=ON)
# Setup google logging after gflags (so that glog can use it).
set(GLOG_DISABLE_OPTIONS "0")

View File

@@ -10,6 +10,7 @@ add_subdirectory(telemetry)
add_subdirectory(communication)
add_subdirectory(stats)
add_subdirectory(auth)
add_subdirectory(tensorflow)
# ----------------------------------------------------------------------------
# Memgraph Single Node

View File

@@ -19,5 +19,7 @@ add_library(mg-communication STATIC ${communication_src_files})
target_link_libraries(mg-communication Threads::Threads mg-utils mg-io fmt glog gflags)
target_link_libraries(mg-communication ${OPENSSL_LIBRARIES})
target_include_directories(mg-communication SYSTEM PUBLIC ${OPENSSL_INCLUDE_DIR})
target_link_libraries(mg-communication capnp kj)
#target_link_libraries(mg-communication capnp kj)
add_dependencies(mg-communication generate_communication_capnp)
target_compile_options(mg-communication PRIVATE -fPIC)

View File

@@ -14,3 +14,5 @@ add_library(mg-io STATIC ${io_src_files})
target_link_libraries(mg-io stdc++fs Threads::Threads fmt glog mg-utils)
target_link_libraries(mg-io capnp kj)
add_dependencies(mg-io generate_io_capnp)
target_compile_options(mg-io PRIVATE -fPIC)

View File

@@ -24,17 +24,17 @@ Endpoint::Endpoint(const std::string &address, uint16_t port)
CHECK(family_ != 0) << "Not a valid IPv4 or IPv6 address: " << address;
}
void Save(const Endpoint &endpoint, capnp::Endpoint::Builder *builder) {
builder->setAddress(endpoint.address());
builder->setPort(endpoint.port());
builder->setFamily(endpoint.family());
}
// void Save(const Endpoint &endpoint, capnp::Endpoint::Builder *builder) {
// builder->setAddress(endpoint.address());
// builder->setPort(endpoint.port());
// builder->setFamily(endpoint.family());
// }
void Load(Endpoint *endpoint, const capnp::Endpoint::Reader &reader) {
endpoint->address_ = reader.getAddress();
endpoint->port_ = reader.getPort();
endpoint->family_ = reader.getFamily();
}
// void Load(Endpoint *endpoint, const capnp::Endpoint::Reader &reader) {
// endpoint->address_ = reader.getAddress();
// endpoint->port_ = reader.getPort();
// endpoint->family_ = reader.getFamily();
// }
bool Endpoint::operator==(const Endpoint &other) const {
return address_ == other.address_ && port_ == other.port_ &&

View File

@@ -5,7 +5,10 @@
#include <iostream>
#include <string>
#if 0
#include "io/network/endpoint.capnp.h"
#endif
#include "utils/exceptions.hpp"
namespace io::network {
@@ -33,8 +36,10 @@ class Endpoint {
unsigned char family_{0};
};
#if 0
void Save(const Endpoint &endpoint, capnp::Endpoint::Builder *builder);
void Load(Endpoint *endpoint, const capnp::Endpoint::Reader &reader);
#endif
} // namespace io::network

View File

@@ -0,0 +1,39 @@
cmake_minimum_required(VERSION 2.8)
set(communication_src_files
${CMAKE_SOURCE_DIR}/src/communication/bolt/v1/value.cpp
${CMAKE_SOURCE_DIR}/src/communication/buffer.cpp
${CMAKE_SOURCE_DIR}/src/communication/client.cpp
${CMAKE_SOURCE_DIR}/src/communication/context.cpp
${CMAKE_SOURCE_DIR}/src/communication/helpers.cpp
${CMAKE_SOURCE_DIR}/src/communication/init.cpp)
set(utils_src_files
${CMAKE_SOURCE_DIR}/src/utils/demangle.cpp
${CMAKE_SOURCE_DIR}/src/utils/file.cpp
${CMAKE_SOURCE_DIR}/src/utils/signals.cpp
${CMAKE_SOURCE_DIR}/src/utils/thread.cpp
${CMAKE_SOURCE_DIR}/src/utils/thread/sync.cpp
${CMAKE_SOURCE_DIR}/src/utils/uuid.cpp
${CMAKE_SOURCE_DIR}/src/utils/watchdog.cpp)
set(io_src_files
${CMAKE_SOURCE_DIR}/src/io/network/addrinfo.cpp
${CMAKE_SOURCE_DIR}/src/io/network/endpoint.cpp
${CMAKE_SOURCE_DIR}/src/io/network/socket.cpp
${CMAKE_SOURCE_DIR}/src/io/network/utils.cpp)
#See https://www.tensorflow.org/how_tos/adding_an_op/
execute_process(COMMAND python3 -c "import tensorflow; print(tensorflow.sysconfig.get_include())" OUTPUT_VARIABLE Tensorflow_INCLUDE_DIRS)
execute_process(COMMAND python3 -c "import tensorflow; print(tensorflow.sysconfig.get_lib() + '/libtensorflow_framework.so', end='')" OUTPUT_VARIABLE Tensorflow_INCLUDE_LIB)
add_library(memgraph_op SHARED memgraph_op.cc ${communication_src_files} ${utils_src_files} ${io_src_files})
target_include_directories(memgraph_op SYSTEM PUBLIC ${Tensorflow_INCLUDE_DIRS})
target_link_libraries(memgraph_op ${Tensorflow_INCLUDE_LIB} mg-utils)
target_link_libraries(memgraph_op stdc++fs Threads::Threads fmt glog gflags uuid)
target_link_libraries(memgraph_op Threads::Threads mg-io fmt glog gflags)
target_link_libraries(memgraph_op ${OPENSSL_LIBRARIES})
target_include_directories(memgraph_op SYSTEM PUBLIC ${OPENSSL_INCLUDE_DIR})
target_compile_definitions(memgraph_op PUBLIC -D_GLIBCXX_USE_CXX11_ABI=0)

View File

@@ -0,0 +1,284 @@
/**
* \file memgraph_op.cc
* \brief Implementation of a memgraph operation in Tensorflow.
*/
#include <fmt/format.h>
#include <string>
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/tensor_shape.h"
#include "tensorflow/core/platform/default/logging.h"
#include "communication/bolt/client.hpp"
#include "communication/bolt/v1/value.hpp"
#include "io/network/endpoint.hpp"
#include "io/network/utils.hpp"
using namespace tensorflow;
const string kHost = "host";
const string kPort = "port";
const string kUser = "user";
const string kPassword = "password";
const string kUseSsl = "use_ssl";
const string kInputList = "input_list";
/**
* @brief Construct attribute definition.
* @details Attribute in TF has special format "key: value = default". This
* function is helper function for constructing attribute definition.
*
* @param key Attribute name
* @param value Attribute value
* @param default_value Default value
* @return "key: value = default_value"
*/
const string Define(const string& key, const string& value,
const string& default_value) {
return key + ": " + value + " = " + default_value;
}
REGISTER_OP("MemgraphOp")
.Attr(Define(kHost, "string", "'127.0.0.1'"))
.Attr(Define(kPort, "int", "7687"))
.Attr(Define(kUser, "string", "''"))
.Attr(Define(kPassword, "string", "''"))
.Attr(Define(kUseSsl, "bool", "false"))
.Attr("output_dtype: {int64, double, bool, string}")
.Input("query: string")
.Input("input_list: int64")
.Output("header: string")
.Output("rows: output_dtype")
.SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) {
::tensorflow::shape_inference::ShapeHandle input;
TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 0, &input));
TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 1, &input));
return Status::OK();
});
/**
* @brief Memgraph Tensorflow Op
* @details Memgraph op is the wrapper around the memgraph client. Memgraph op
* takes attributes for connection information and one attribute for output type
* definition: int64, double, bool, string. There are two inputs: query (string)
* and the input list (int64 list). The user can use the input list in the query
* with variable $input_list. There are two outputs: header and rows. Headers
* are names of the columns in the output table and rows are all data fetch from
* memgraph with the query. Memgraph Op has one limitation on output. All output
* values in rows data must have the same type. If the user set output type to
* string, then all data convert to the string, however, in any other case
* error appears (the implicit cast is not possible for other types). List will
* be expand into the matrix. All rows must contains lists with same size.
*
*
* @tparam T Output type
*/
template <typename T>
class MemgraphOp final : public OpKernel {
private:
const string kBoltClientVersion = "TensorflowMemgraphOp"; // TODO version?
string host_;
int port_;
string user_;
string password_;
bool use_ssl_;
io::network::Endpoint endpoint_;
communication::bolt::Client* client_;
T GetValue(const communication::bolt::Value& value);
string ToString(const communication::bolt::Value& value) {
if (value.IsString())
return value.ValueString();
else {
std::stringstream stream;
stream << value;
return stream.str();
}
}
string ToString(const communication::bolt::Value::Type& type) {
std::stringstream stream;
stream << type;
return stream.str();
}
public:
/**
* @brief Constructor
* @details Instance will connect to memgraph.
*
* @param context Context contains attribute values - database connection
* information and output data type.
*/
explicit MemgraphOp(OpKernelConstruction* context) : OpKernel(context) {
OP_REQUIRES_OK(context, context->GetAttr(kHost, &host_));
OP_REQUIRES_OK(context, context->GetAttr(kPort, &port_));
OP_REQUIRES_OK(context, context->GetAttr(kUser, &user_));
OP_REQUIRES_OK(context, context->GetAttr(kPassword, &password_));
OP_REQUIRES_OK(context, context->GetAttr(kUseSsl, &use_ssl_));
communication::Init();
endpoint_ =
io::network::Endpoint(io::network::ResolveHostname(host_), port_);
communication::ClientContext context_mg(use_ssl_);
client_ = new communication::bolt::Client(&context_mg);
OP_REQUIRES(context, client_ != NULL,
errors::Internal("Cannot create client instance"));
try {
client_->Connect(endpoint_, user_, password_, kBoltClientVersion);
} catch (const communication::bolt::ClientFatalException& e) {
OP_REQUIRES(context, false,
errors::Internal(
fmt::format("Cannot connect to memgraph: {}", e.what())));
}
}
/**
* @brief Compute stage
* @details Op reads input data (query and input list), executes query and on
* fills the outputs.
*
* @param context Context contains data for inputs and outputs.
*/
void Compute(OpKernelContext* context) override {
const Tensor& param_tensor = context->input(1);
auto params = param_tensor.flat<int64>();
std::vector<communication::bolt::Value> input_list;
for (size_t i = 0; i < params.size(); ++i) {
communication::bolt::Value value(static_cast<int64_t>(params(i)));
input_list.push_back(value);
}
const Tensor& input_tensor = context->input(0);
auto query = input_tensor.flat<string>()(0);
string message;
communication::bolt::QueryData ret;
try {
ret = client_->Execute(query, {{kInputList, input_list}});
} catch (communication::bolt::ClientQueryException& e) {
client_->Close();
message = fmt::format("Query error: {}", e.what());
OP_REQUIRES(context, false, errors::Internal(message));
} catch (const communication::bolt::ClientFatalException& e) {
client_->Close();
message = fmt::format("Internal error: {}", e.what());
bool is_connected = false;
try {
client_->Connect(endpoint_, user_, password_, kBoltClientVersion);
ret = client_->Execute(query, {{kInputList, input_list}});
is_connected = true;
} catch (communication::bolt::ClientQueryException& e) {
} catch (const communication::bolt::ClientFatalException& e) {
}
OP_REQUIRES(context, is_connected, errors::Internal(message));
}
// Use first row to find out Tensor size (inner lists).
size_t row_width = 0;
size_t row_height = 0;
std::vector<size_t> column_list_size(ret.fields.size(), 0);
if (ret.records.size() > 0) {
for (size_t j = 0, i = 0; j < ret.records[i].size(); ++j) {
const auto& field = ret.records[i][j];
if (field.IsList())
column_list_size[j] = field.ValueList().size();
else
column_list_size[j] = 1;
row_width += column_list_size[j];
}
}
if (ret.records.size() == 1 && row_width == 0)
row_height = 0;
else
row_height = ret.records.size();
// Create header output
TensorShape header_output__shape;
header_output__shape.AddDim(row_width);
Tensor* header_output = NULL;
OP_REQUIRES_OK(context, context->allocate_output(0, header_output__shape,
&header_output));
auto header_output_flat = header_output->flat<string>();
for (size_t i = 0, cnt = 0; i < ret.fields.size(); ++i)
if (column_list_size[i] == 1)
header_output_flat(cnt++) = ret.fields[i];
else
for (size_t j = 0; j < column_list_size[i]; ++j)
header_output_flat(cnt++) = ret.fields[i] + "_" + std::to_string(j);
// Create row output
TensorShape rows_output_shape;
rows_output_shape.AddDim(row_height);
rows_output_shape.AddDim(row_width);
Tensor* rows_output = NULL;
OP_REQUIRES_OK(
context, context->allocate_output(1, rows_output_shape, &rows_output));
auto rows_output_matrix = rows_output->matrix<T>();
for (size_t i = 0; i < row_height; ++i) {
for (size_t j = 0, cnt = 0; j < ret.records[i].size(); ++j) {
const auto& field = ret.records[i][j];
try {
if (field.IsList()) {
OP_REQUIRES(context,
column_list_size[j] == field.ValueList().size(),
errors::Internal(fmt::format(
"List has wrong size, row: {}, header: {}", i,
ret.fields[j])));
for (size_t k = 0; k < column_list_size[j]; ++k) {
rows_output_matrix(i, cnt++) = GetValue(field.ValueList().at(k));
}
} else {
rows_output_matrix(i, cnt++) = GetValue(field);
}
} catch (const communication::bolt::ValueException e) {
string message =
fmt::format("Wrong type: {} = {} ({})", header_output_flat(cnt),
field.type(), field);
OP_REQUIRES(context, false, errors::Internal(message));
}
}
}
}
};
template <>
int64 MemgraphOp<int64>::GetValue(const communication::bolt::Value& value) {
return value.ValueInt();
}
template <>
double MemgraphOp<double>::GetValue(const communication::bolt::Value& value) {
return value.ValueDouble();
}
template <>
bool MemgraphOp<bool>::GetValue(const communication::bolt::Value& value) {
return value.ValueBool();
}
template <>
string MemgraphOp<string>::GetValue(const communication::bolt::Value& value) {
return ToString(value);
}
REGISTER_KERNEL_BUILDER(
Name("MemgraphOp").Device(DEVICE_CPU).TypeConstraint<int64>("output_dtype"),
MemgraphOp<int64>);
REGISTER_KERNEL_BUILDER(Name("MemgraphOp")
.Device(DEVICE_CPU)
.TypeConstraint<double>("output_dtype"),
MemgraphOp<double>);
REGISTER_KERNEL_BUILDER(
Name("MemgraphOp").Device(DEVICE_CPU).TypeConstraint<bool>("output_dtype"),
MemgraphOp<bool>);
REGISTER_KERNEL_BUILDER(Name("MemgraphOp")
.Device(DEVICE_CPU)
.TypeConstraint<string>("output_dtype"),
MemgraphOp<string>);

46
src/tensorflow/test.py Executable file
View File

@@ -0,0 +1,46 @@
#!/usr/bin/env python3
import tensorflow as tf
# Load libmemgraph_op.so
memgraph_op_module = tf.load_op_library('libmemgraph_op.so')
def main():
query = """match (u :User)-->(m :Movie)
where u.id in $input_list
return u.id, m.id;"""
# Input list used in query
input_list = [1, 2, 3, 4, 5]
# Create tensorflow session
with tf.Session() as sess:
# Query placeholder
query_holder = tf.placeholder(tf.string)
# Input list placeholder
input_list_holder = tf.placeholder(tf.int64)
# Create Memgraph op, and put placeholders for input
memgraph_op = memgraph_op_module.memgraph_op(query_holder,
input_list_holder,
output_dtype=tf.int64)
# Run Memgraph op
output = sess.run(memgraph_op, {query_holder: query,
input_list_holder: input_list})
# First output is list of headers
print("Headers:")
for i in output[0]:
print(i)
# Output matrix (rows), query results
print("Rows: ")
for i in output[1]:
print(i)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,105 @@
import unittest
import tensorflow as tf
import numpy as np
class MemgrapOp:
def __init__(self, path_to_lib):
self.__memgraph_op_module = tf.load_op_library(path_to_lib)
def runQuery(self, query, type, input_list=[]):
with tf.Session() as sess:
query_holder = tf.placeholder(tf.string)
input_list_holder = tf.placeholder(tf.int64)
memgraph_op = self.__memgraph_op_module.memgraph_op(query_holder, input_list_holder, output_dtype=type)
return sess.run(memgraph_op, {query_holder: query, input_list_holder: input_list})
def runQueryAdvance(self, query, input_list = [], **kargs):
with tf.Session() as sess:
query_holder = tf.placeholder(tf.string)
input_list_holder = tf.placeholder(tf.int64)
memgraph_op = self.__memgraph_op_module.memgraph_op(query_holder, input_list_holder, **kargs)
return sess.run(memgraph_op, {query_holder: query, input_list_holder: input_list})
class MemgraphOpTest(unittest.TestCase):
def assertRowEqual(self, expected, result):
np.testing.assert_array_equal(expected, result)
def setUp(self):
self.memgraph_op = MemgrapOp('../../../build/src/tensorflow/libmemgraph_op.so')
def test_output_int64(self):
header, rows = self.memgraph_op.runQuery("MATCH (n :User) RETURN n.id AS id ORDER BY id LIMIT 5;", tf.int64)
self.assertEqual(header, b'id')
self.assertRowEqual(rows, [[1],[2],[3],[4],[5]])
def test_output_double(self):
header, rows = self.memgraph_op.runQuery("MATCH (n :User)-[r :Rating]-(:Movie) WHERE r.score < 5 RETURN r.score AS score ORDER BY score DESC LIMIT 5;", tf.double)
self.assertEqual(header, b'score')
self.assertRowEqual(rows, [[4.5],[4.5],[4.5],[4.5],[4.5]])
def test_output_bool(self):
header, rows = self.memgraph_op.runQuery("MATCH (n :User) RETURN n.name = 'Albert' AS eq ORDER BY n.name LIMIT 5", tf.bool)
self.assertEqual(header, b'eq')
self.assertRowEqual(rows, [[False], [True], [True], [False], [False]])
def test_output_string(self):
header, rows = self.memgraph_op.runQuery("MATCH (n :User) RETURN n.name AS name ORDER BY name DESC LIMIT 5", tf.string)
self.assertEqual(header, b'name')
self.assertRowEqual(rows, [[b'Winston'], [b'Winny'], [b'Winnifred'], [b'Winnie'], [b'Winifred']])
def test_input_list(self):
header, rows = self.memgraph_op.runQuery("MATCH (n :User) WHERE n.id IN [25,35,45,55,65] RETURN n.name AS name ORDER BY name ASC", tf.string)
self.assertEqual(header, b'name')
self.assertRowEqual(rows, [[b'Benny'],[b'Bernard'],[b'Beverly'],[b'Gilbert'],[b'Wilhelmina']])
def test_matrix_out(self):
header, rows = self.memgraph_op.runQuery("MATCH (n :User)-->(m :Movie) RETURN n.id AS NID, m.id AS MID ORDER BY NID, MID LIMIT 10", tf.int64)
self.assertRowEqual(header, [b'NID', b'MID'])
self.assertRowEqual(rows,[[2,62],[2,110],[2,223],[2,261],[2,319],[2,527],[3,110],[3,527],[4,173],[4,260]])
def test_connection_host(self):
runner = lambda : self.memgraph_op.runQueryAdvance("RETURN 1;", output_dtype=tf.int64, host = "10.0.0.1", port = 1234)
with self.assertRaises(Exception) as context:
runner()
print(str(context.exception))
self.assertTrue('Cannot connect to memgraph' in str(context.exception))
def test_connection_port(self):
runner = lambda : self.memgraph_op.runQueryAdvance("RETURN 1;", output_dtype=tf.int64, port = 8888)
with self.assertRaises(Exception) as context:
runner()
self.assertTrue('Cannot connect to memgraph' in str(context.exception))
def test_connection_ssl(self):
runner = lambda : self.memgraph_op.runQueryAdvance("RETURN 1;", output_dtype=tf.int64, use_ssl = True)
with self.assertRaises(Exception) as context:
runner()
print(str(context.exception))
self.assertTrue('Cannot connect to memgraph' in str(context.exception))
def test_wrong_query(self):
runner = lambda : self.memgraph_op.runQueryAdvance("SELECT * FROM table", output_dtype=tf.int64)
with self.assertRaises(Exception) as context:
runner()
self.assertTrue('Query error' in str(context.exception))
def test_wrong_list_size(self):
runner = lambda : self.memgraph_op.runQueryAdvance("MATCH (n :User)-->(m :Movie) RETURN n.id, collect(m.id);", output_dtype=tf.int64)
with self.assertRaises(Exception) as context:
runner()
self.assertTrue('List has wrong size' in str(context.exception))
def test_different_types(self):
runner = lambda : self.memgraph_op.runQueryAdvance("MATCH (n :User)-->(m :Movie) RETURN n.id, m.title;", output_dtype=tf.int64)
with self.assertRaises(Exception) as context:
runner()
self.assertTrue('Wrong type' in str(context.exception))
def test_different_types_string(self):
header, rows = self.memgraph_op.runQueryAdvance("MATCH (n :User)-->(m :Movie) RETURN n.id AS NID, m.title AS TITLE ORDER BY NID, TITLE LIMIT 2;", output_dtype=tf.string)
self.assertRowEqual(header, [b'NID', b'TITLE'])
self.assertRowEqual(rows, [[b'2', b'2001: A Space Odyssey'], [b'2', b'Cat on a Hot Tin Roof']])

View File

@@ -0,0 +1,23 @@
#!/bin/bash
MG_PATH='../../../build/memgraph'
MG_CLIENT_PATH='../../../build/tools/src/mg_client'
DATA_PATH='../../../tests/qa/tck_engine/tests/movie_example/graphs/movies_graph.cypher'
# Run Memgraph
$MG_PATH &
MG_PID=$!
disown
echo "Memgraph running with pid $MG_PID"
echo "Loading data into Memgraph"
sleep 1 # So Memgraph has time to start
$MG_CLIENT_PATH --use-ssl=false < $DATA_PATH
echo "Testing..."
python3 -m unittest integration_test.py
echo "Done."
kill $MG_PID

View File

@@ -9,11 +9,13 @@ set(utils_src_files
define_add_capnp(add_capnp utils_src_files utils_capnp_files)
add_capnp(serialization.capnp)
#add_capnp(serialization.capnp)
add_custom_target(generate_utils_capnp DEPENDS ${utils_capnp_files})
#add_custom_target(generate_utils_capnp DEPENDS ${utils_capnp_files})
add_library(mg-utils STATIC ${utils_src_files})
target_link_libraries(mg-utils stdc++fs Threads::Threads fmt glog gflags uuid)
target_link_libraries(mg-utils capnp kj)
add_dependencies(mg-utils generate_utils_capnp)
#target_link_libraries(mg-utils capnp kj)
#add_dependencies(mg-utils generate_utils_capnp)
target_compile_options(mg-utils PRIVATE -fPIC)

47
src/utils/dector.hpp Normal file
View File

@@ -0,0 +1,47 @@
#pragma once
#include <fstream>
#include <iostream>
#include <map>
#include <set>
#include <vector>
namespace profiler {
struct Package {
std::string name;
size_t size;
size_t capacity;
};
extern std::vector<Package> profiler_info;
inline void WriteToFile(const std::string &file_name) {
std::ofstream file;
file.open(file_name);
for (const auto &package : profiler_info) {
file << package.name << "\t" << package.size << "\t" << package.capacity
<< std::endl;
}
file.close();
}
template <typename T>
class dector : public std::vector<T> {
private:
std::string file_;
int line_;
public:
dector(std::string file, int line) : file_(file), line_(line) {}
~dector() {
std::string vector_name = file_ + ":" + std::to_string(line_);
profiler::Package package{vector_name, this->size(), this->capacity()};
profiler::profiler_info.push_back(package);
}
void SetName(std::string file, int line) {
this->file_ = file;
this->line_ = line;
}
};
}