368 lines
13 KiB
C++
368 lines
13 KiB
C++
// Licensed to the Apache Software Foundation (ASF) under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
// to you under the Apache License, Version 2.0 (the
|
|
// "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <iostream>
|
|
#include <gtest/gtest.h>
|
|
#include <boost/bind.hpp>
|
|
#include "common/object_pool.h"
|
|
#include "util/runtime_profile.h"
|
|
#include "util/cpu_info.h"
|
|
|
|
using namespace std;
|
|
using namespace boost;
|
|
|
|
namespace impala {
|
|
|
|
TEST(CountersTest, Basic) {
|
|
ObjectPool pool;
|
|
RuntimeProfile profile_a(&pool, "ProfileA");
|
|
RuntimeProfile profile_a1(&pool, "ProfileA1");
|
|
RuntimeProfile profile_a2(&pool, "ProfileAb");
|
|
|
|
TRuntimeProfileTree thrift_profile;
|
|
|
|
profile_a.AddChild(&profile_a1);
|
|
profile_a.AddChild(&profile_a2);
|
|
|
|
// Test Empty
|
|
profile_a.ToThrift(&thrift_profile.nodes);
|
|
EXPECT_EQ(thrift_profile.nodes.size(), 3);
|
|
thrift_profile.nodes.clear();
|
|
|
|
RuntimeProfile::Counter* counter_a;
|
|
RuntimeProfile::Counter* counter_b;
|
|
RuntimeProfile::Counter* counter_merged;
|
|
|
|
// Updating/setting counter
|
|
counter_a = profile_a.AddCounter("A", TCounterType::UNIT);
|
|
EXPECT_TRUE(counter_a != NULL);
|
|
counter_a->Update(10);
|
|
counter_a->Update(-5);
|
|
EXPECT_EQ(counter_a->value(), 5);
|
|
counter_a->Set(1L);
|
|
EXPECT_EQ(counter_a->value(), 1);
|
|
|
|
counter_b = profile_a2.AddCounter("B", TCounterType::BYTES);
|
|
EXPECT_TRUE(counter_b != NULL);
|
|
|
|
// Serialize/deserialize
|
|
profile_a.ToThrift(&thrift_profile.nodes);
|
|
RuntimeProfile* from_thrift = RuntimeProfile::CreateFromThrift(&pool, thrift_profile);
|
|
counter_merged = from_thrift->GetCounter("A");
|
|
EXPECT_EQ(counter_merged->value(), 1);
|
|
EXPECT_TRUE(from_thrift->GetCounter("Not there") == NULL);
|
|
|
|
// Merge
|
|
RuntimeProfile merged_profile(&pool, "Merged");
|
|
merged_profile.Merge(from_thrift);
|
|
counter_merged = merged_profile.GetCounter("A");
|
|
EXPECT_EQ(counter_merged->value(), 1);
|
|
|
|
// Merge 2 more times, counters should get aggregated
|
|
merged_profile.Merge(from_thrift);
|
|
merged_profile.Merge(from_thrift);
|
|
EXPECT_EQ(counter_merged->value(), 3);
|
|
|
|
// Update
|
|
RuntimeProfile updated_profile(&pool, "Updated");
|
|
updated_profile.Update(thrift_profile);
|
|
RuntimeProfile::Counter* counter_updated = updated_profile.GetCounter("A");
|
|
EXPECT_EQ(counter_updated->value(), 1);
|
|
|
|
// Update 2 more times, counters should stay the same
|
|
updated_profile.Update(thrift_profile);
|
|
updated_profile.Update(thrift_profile);
|
|
EXPECT_EQ(counter_updated->value(), 1);
|
|
}
|
|
|
|
void ValidateCounter(RuntimeProfile* profile, const string& name, int64_t value) {
|
|
RuntimeProfile::Counter* counter = profile->GetCounter(name);
|
|
EXPECT_TRUE(counter != NULL);
|
|
EXPECT_EQ(counter->value(), value);
|
|
}
|
|
|
|
TEST(CountersTest, MergeAndUpdate) {
|
|
// Create two trees. Each tree has two children, one of which has the
|
|
// same name in both trees. Merging the two trees should result in 3
|
|
// children, with the counters from the shared child aggregated.
|
|
|
|
ObjectPool pool;
|
|
RuntimeProfile profile1(&pool, "Parent1");
|
|
RuntimeProfile p1_child1(&pool, "Child1");
|
|
RuntimeProfile p1_child2(&pool, "Child2");
|
|
profile1.AddChild(&p1_child1);
|
|
profile1.AddChild(&p1_child2);
|
|
|
|
RuntimeProfile profile2(&pool, "Parent2");
|
|
RuntimeProfile p2_child1(&pool, "Child1");
|
|
RuntimeProfile p2_child3(&pool, "Child3");
|
|
profile2.AddChild(&p2_child1);
|
|
profile2.AddChild(&p2_child3);
|
|
|
|
// Create parent level counters
|
|
RuntimeProfile::Counter* parent1_shared =
|
|
profile1.AddCounter("Parent Shared", TCounterType::UNIT);
|
|
RuntimeProfile::Counter* parent2_shared =
|
|
profile2.AddCounter("Parent Shared", TCounterType::UNIT);
|
|
RuntimeProfile::Counter* parent1_only =
|
|
profile1.AddCounter("Parent 1 Only", TCounterType::UNIT);
|
|
RuntimeProfile::Counter* parent2_only =
|
|
profile2.AddCounter("Parent 2 Only", TCounterType::UNIT);
|
|
parent1_shared->Update(1);
|
|
parent2_shared->Update(3);
|
|
parent1_only->Update(2);
|
|
parent2_only->Update(5);
|
|
|
|
// Create child level counters
|
|
RuntimeProfile::Counter* p1_c1_shared =
|
|
p1_child1.AddCounter("Child1 Shared", TCounterType::UNIT);
|
|
RuntimeProfile::Counter* p1_c1_only =
|
|
p1_child1.AddCounter("Child1 Parent 1 Only", TCounterType::UNIT);
|
|
RuntimeProfile::Counter* p1_c2 =
|
|
p1_child2.AddCounter("Child2", TCounterType::UNIT);
|
|
RuntimeProfile::Counter* p2_c1_shared =
|
|
p2_child1.AddCounter("Child1 Shared", TCounterType::UNIT);
|
|
RuntimeProfile::Counter* p2_c1_only =
|
|
p1_child1.AddCounter("Child1 Parent 2 Only", TCounterType::UNIT);
|
|
RuntimeProfile::Counter* p2_c3 =
|
|
p2_child3.AddCounter("Child3", TCounterType::UNIT);
|
|
p1_c1_shared->Update(10);
|
|
p1_c1_only->Update(50);
|
|
p2_c1_shared->Update(20);
|
|
p2_c1_only->Update(100);
|
|
p2_c3->Update(30);
|
|
p1_c2->Update(40);
|
|
|
|
// Merge the two and validate
|
|
TRuntimeProfileTree tprofile1;
|
|
profile1.ToThrift(&tprofile1);
|
|
RuntimeProfile* merged_profile = RuntimeProfile::CreateFromThrift(&pool, tprofile1);
|
|
merged_profile->Merge(&profile2);
|
|
EXPECT_EQ(4, merged_profile->num_counters());
|
|
ValidateCounter(merged_profile, "Parent Shared", 4);
|
|
ValidateCounter(merged_profile, "Parent 1 Only", 2);
|
|
ValidateCounter(merged_profile, "Parent 2 Only", 5);
|
|
|
|
vector<RuntimeProfile*> children;
|
|
merged_profile->GetChildren(&children);
|
|
EXPECT_EQ(children.size(), 3);
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
RuntimeProfile* profile = children[i];
|
|
|
|
if (profile->name().compare("Child1") == 0) {
|
|
EXPECT_EQ(4, profile->num_counters());
|
|
ValidateCounter(profile, "Child1 Shared", 30);
|
|
ValidateCounter(profile, "Child1 Parent 1 Only", 50);
|
|
ValidateCounter(profile, "Child1 Parent 2 Only", 100);
|
|
} else if (profile->name().compare("Child2") == 0) {
|
|
EXPECT_EQ(2, profile->num_counters());
|
|
ValidateCounter(profile, "Child2", 40);
|
|
} else if (profile->name().compare("Child3") == 0) {
|
|
EXPECT_EQ(2, profile->num_counters());
|
|
ValidateCounter(profile, "Child3", 30);
|
|
} else {
|
|
EXPECT_TRUE(false);
|
|
}
|
|
}
|
|
|
|
// make sure we can print
|
|
stringstream dummy;
|
|
merged_profile->PrettyPrint(&dummy);
|
|
|
|
// Update profile2 w/ profile1 and validate
|
|
profile2.Update(tprofile1);
|
|
EXPECT_EQ(4, profile2.num_counters());
|
|
ValidateCounter(&profile2, "Parent Shared", 1);
|
|
ValidateCounter(&profile2, "Parent 1 Only", 2);
|
|
ValidateCounter(&profile2, "Parent 2 Only", 5);
|
|
|
|
profile2.GetChildren(&children);
|
|
EXPECT_EQ(children.size(), 3);
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
RuntimeProfile* profile = children[i];
|
|
|
|
if (profile->name().compare("Child1") == 0) {
|
|
EXPECT_EQ(4, profile->num_counters());
|
|
ValidateCounter(profile, "Child1 Shared", 10);
|
|
ValidateCounter(profile, "Child1 Parent 1 Only", 50);
|
|
ValidateCounter(profile, "Child1 Parent 2 Only", 100);
|
|
} else if (profile->name().compare("Child2") == 0) {
|
|
EXPECT_EQ(2, profile->num_counters());
|
|
ValidateCounter(profile, "Child2", 40);
|
|
} else if (profile->name().compare("Child3") == 0) {
|
|
EXPECT_EQ(2, profile->num_counters());
|
|
ValidateCounter(profile, "Child3", 30);
|
|
} else {
|
|
EXPECT_TRUE(false);
|
|
}
|
|
}
|
|
|
|
// make sure we can print
|
|
profile2.PrettyPrint(&dummy);
|
|
}
|
|
|
|
TEST(CountersTest, DerivedCounters) {
|
|
ObjectPool pool;
|
|
RuntimeProfile profile(&pool, "Profile");
|
|
RuntimeProfile::Counter* bytes_counter =
|
|
profile.AddCounter("bytes", TCounterType::BYTES);
|
|
RuntimeProfile::Counter* ticks_counter =
|
|
profile.AddCounter("ticks", TCounterType::TIME_NS);
|
|
// set to 1 sec
|
|
ticks_counter->Set(1000L * 1000L * 1000L);
|
|
|
|
RuntimeProfile::DerivedCounter* throughput_counter =
|
|
profile.AddDerivedCounter("throughput", TCounterType::BYTES,
|
|
bind<int64_t>(&RuntimeProfile::UnitsPerSecond, bytes_counter, ticks_counter));
|
|
|
|
bytes_counter->Set(10L);
|
|
EXPECT_EQ(throughput_counter->value(), 10);
|
|
bytes_counter->Set(20L);
|
|
EXPECT_EQ(throughput_counter->value(), 20);
|
|
ticks_counter->Set(ticks_counter->value() / 2);
|
|
EXPECT_EQ(throughput_counter->value(), 40);
|
|
}
|
|
|
|
TEST(CountersTest, InfoStringTest) {
|
|
ObjectPool pool;
|
|
RuntimeProfile profile(&pool, "Profile");
|
|
EXPECT_TRUE(profile.GetInfoString("Key") == NULL);
|
|
|
|
profile.AddInfoString("Key", "Value");
|
|
const string* value = profile.GetInfoString("Key");
|
|
EXPECT_TRUE(value != NULL);
|
|
EXPECT_EQ(*value, "Value");
|
|
|
|
// Convert it to thrift
|
|
TRuntimeProfileTree tprofile;
|
|
profile.ToThrift(&tprofile);
|
|
|
|
// Convert it back
|
|
RuntimeProfile* from_thrift = RuntimeProfile::CreateFromThrift(
|
|
&pool, tprofile);
|
|
value = from_thrift->GetInfoString("Key");
|
|
EXPECT_TRUE(value != NULL);
|
|
EXPECT_EQ(*value, "Value");
|
|
|
|
// Test update.
|
|
RuntimeProfile update_dst_profile(&pool, "Profile2");
|
|
update_dst_profile.Update(tprofile);
|
|
value = update_dst_profile.GetInfoString("Key");
|
|
EXPECT_TRUE(value != NULL);
|
|
EXPECT_EQ(*value, "Value");
|
|
|
|
// Update the original profile, convert it to thrift and update from the dst
|
|
// profile
|
|
profile.AddInfoString("Key", "NewValue");
|
|
profile.AddInfoString("Foo", "Bar");
|
|
EXPECT_EQ(*profile.GetInfoString("Key"), "NewValue");
|
|
EXPECT_EQ(*profile.GetInfoString("Foo"), "Bar");
|
|
profile.ToThrift(&tprofile);
|
|
|
|
update_dst_profile.Update(tprofile);
|
|
EXPECT_EQ(*update_dst_profile.GetInfoString("Key"), "NewValue");
|
|
EXPECT_EQ(*update_dst_profile.GetInfoString("Foo"), "Bar");
|
|
}
|
|
|
|
TEST(CountersTest, RateCounters) {
|
|
ObjectPool pool;
|
|
RuntimeProfile profile(&pool, "Profile");
|
|
|
|
RuntimeProfile::Counter* bytes_counter =
|
|
profile.AddCounter("bytes", TCounterType::BYTES);
|
|
|
|
RuntimeProfile::Counter* rate_counter =
|
|
profile.AddRateCounter("RateCounter", bytes_counter);
|
|
EXPECT_TRUE(rate_counter->type() == TCounterType::BYTES_PER_SECOND);
|
|
|
|
EXPECT_EQ(rate_counter->value(), 0);
|
|
// set to 100MB. Use bigger units to avoid truncating to 0 after divides.
|
|
bytes_counter->Set(100L * 1024L * 1024L);
|
|
|
|
// Wait one second.
|
|
sleep(1);
|
|
|
|
int64_t rate = rate_counter->value();
|
|
|
|
// Remove the counter so it no longer gets updates
|
|
profile.StopRateCounterUpdates(rate_counter);
|
|
|
|
// The rate counter is not perfectly accurate. Currently updated at 500ms intervals,
|
|
// we should have seen somewhere between 1 and 3 updates (33 - 200 MB/s)
|
|
EXPECT_GT(rate, 66 * 1024 * 1024);
|
|
EXPECT_LE(rate, 200 * 1024 * 1024);
|
|
|
|
// Wait another second. The counter has been removed. So the value should not be
|
|
// changed (much).
|
|
sleep(2);
|
|
|
|
rate = rate_counter->value();
|
|
EXPECT_GT(rate, 66 * 1024 * 1024);
|
|
EXPECT_LE(rate, 200 * 1024 * 1024);
|
|
}
|
|
|
|
TEST(CountersTest, BucketCounters) {
|
|
ObjectPool pool;
|
|
RuntimeProfile profile(&pool, "Profile");
|
|
|
|
RuntimeProfile::Counter* unit_counter =
|
|
profile.AddCounter("unit", TCounterType::UNIT);
|
|
|
|
// Set the unit to 1 before sampling
|
|
unit_counter->Set(1L);
|
|
|
|
// Create the bucket counters and start sampling
|
|
vector<RuntimeProfile::Counter*> buckets;
|
|
profile.AddBucketingCounters("BucketCounters", "", unit_counter, 2, &buckets);
|
|
|
|
// Wait two seconds.
|
|
sleep(2);
|
|
|
|
// Stop sampling
|
|
profile.StopBucketingCountersUpdates(&buckets, true);
|
|
|
|
// TODO: change the value to double
|
|
// The value of buckets[0] should be zero and buckets[1] should be 1.
|
|
double val0 = buckets[0]->double_value();
|
|
double val1 = buckets[1]->double_value();
|
|
EXPECT_EQ(0, val0);
|
|
EXPECT_EQ(100, val1);
|
|
|
|
// Wait another second. The counter has been removed. So the value should not be
|
|
// changed (much).
|
|
sleep(2);
|
|
EXPECT_EQ(val0, buckets[0]->double_value());
|
|
EXPECT_EQ(val1, buckets[1]->double_value());
|
|
}
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
std::string conffile = std::string(getenv("DORIS_HOME")) + "/conf/be.conf";
|
|
if (!doris::config::init(conffile.c_str(), false)) {
|
|
fprintf(stderr, "error read config file. \n");
|
|
return -1;
|
|
}
|
|
init_glog("be-test");
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
impala::CpuInfo::Init();
|
|
return RUN_ALL_TESTS();
|
|
}
|