Merge branch 'DRTVWR-516-maint' of https://bitbucket.org/lindenlab/viewer
commit
584ec4b4cc
|
|
@ -3035,6 +3035,10 @@ if (LL_TESTS)
|
|||
${BOOST_CONTEXT_LIBRARY}
|
||||
)
|
||||
|
||||
LL_ADD_INTEGRATION_TEST(cppfeatures
|
||||
""
|
||||
"${test_libs}"
|
||||
)
|
||||
LL_ADD_INTEGRATION_TEST(llsechandler_basic
|
||||
llsechandler_basic.cpp
|
||||
"${test_libs}"
|
||||
|
|
|
|||
|
|
@ -2135,6 +2135,10 @@ void LLEnvironment::coroRequestEnvironment(S32 parcel_id, LLEnvironment::environ
|
|||
{
|
||||
LL_WARNS("ENVIRONMENT") << "Couldn't retrieve environment settings for " << ((parcel_id == INVALID_PARCEL_ID) ? ("region!") : ("parcel!")) << LL_ENDL;
|
||||
}
|
||||
else if (LLApp::isExiting())
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LLSD environment = result[KEY_ENVIRONMENT];
|
||||
|
|
@ -2224,6 +2228,10 @@ void LLEnvironment::coroUpdateEnvironment(S32 parcel_id, S32 track_no, UpdateInf
|
|||
notify = LLSD::emptyMap();
|
||||
notify["FAIL_REASON"] = result["message"].asString();
|
||||
}
|
||||
else if (LLApp::isExiting())
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LLSD environment = result[KEY_ENVIRONMENT];
|
||||
|
|
@ -2286,6 +2294,10 @@ void LLEnvironment::coroResetEnvironment(S32 parcel_id, S32 track_no, environmen
|
|||
notify = LLSD::emptyMap();
|
||||
notify["FAIL_REASON"] = result["message"].asString();
|
||||
}
|
||||
else if (LLApp::isExiting())
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LLSD environment = result[KEY_ENVIRONMENT];
|
||||
|
|
|
|||
|
|
@ -225,23 +225,15 @@ void LLLandmarkList::onRegionHandle(const LLUUID& landmark_id)
|
|||
{
|
||||
LLLandmark* landmark = getAsset(landmark_id);
|
||||
|
||||
if (!landmark)
|
||||
{
|
||||
LL_WARNS() << "Got region handle but the landmark not found." << LL_ENDL;
|
||||
mLoadedCallbackMap.erase(landmark_id); // <FS:Ansariel> Clean up callback map
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate landmark global position.
|
||||
// This should succeed since the region handle is available.
|
||||
LLVector3d pos;
|
||||
if (!landmark->getGlobalPos(pos))
|
||||
if (landmark && !landmark->getGlobalPos(pos))
|
||||
{
|
||||
LL_WARNS() << "Got region handle but the landmark global position is still unknown." << LL_ENDL;
|
||||
mLoadedCallbackMap.erase(landmark_id); // <FS:Ansariel> Clean up callback map
|
||||
return;
|
||||
LL_WARNS() << "Got region handle but the landmark " << landmark_id << " global position is still unknown." << LL_ENDL;
|
||||
}
|
||||
|
||||
// Call this even if no landmark exists to clean mLoadedCallbackMap
|
||||
makeCallbacks(landmark_id);
|
||||
}
|
||||
|
||||
|
|
@ -251,7 +243,7 @@ void LLLandmarkList::makeCallbacks(const LLUUID& landmark_id)
|
|||
|
||||
if (!landmark)
|
||||
{
|
||||
LL_WARNS() << "Landmark to make callbacks for not found." << LL_ENDL;
|
||||
LL_WARNS() << "Landmark " << landmark_id << " to make callbacks for not found." << LL_ENDL;
|
||||
}
|
||||
|
||||
// make all the callbacks here.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,386 @@
|
|||
/**
|
||||
* @file cppfeatures_test
|
||||
* @author Vir
|
||||
* @date 2021-03
|
||||
* @brief cpp features
|
||||
*
|
||||
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2021, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
// Tests related to newer C++ features, for verifying support across compilers and platforms
|
||||
|
||||
#include "linden_common.h"
|
||||
#include "../test/lltut.h"
|
||||
|
||||
namespace tut
|
||||
{
|
||||
|
||||
struct cpp_features_test {};
|
||||
typedef test_group<cpp_features_test> cpp_features_test_t;
|
||||
typedef cpp_features_test_t::object cpp_features_test_object_t;
|
||||
tut::cpp_features_test_t tut_cpp_features_test("LLCPPFeatures");
|
||||
|
||||
// bracket initializers
|
||||
// Can initialize containers or values using curly brackets
|
||||
template<> template<>
|
||||
void cpp_features_test_object_t::test<1>()
|
||||
{
|
||||
S32 explicit_val{3};
|
||||
ensure(explicit_val==3);
|
||||
|
||||
S32 default_val{};
|
||||
ensure(default_val==0);
|
||||
|
||||
std::vector<S32> fibs{1,1,2,3,5};
|
||||
ensure(fibs[4]==5);
|
||||
}
|
||||
|
||||
// auto
|
||||
//
|
||||
// https://en.cppreference.com/w/cpp/language/auto
|
||||
//
|
||||
// Can use auto in place of a more complex type specification, if the compiler can infer the type
|
||||
template<> template<>
|
||||
void cpp_features_test_object_t::test<2>()
|
||||
{
|
||||
std::vector<S32> numbers{3,6,9};
|
||||
|
||||
// auto element
|
||||
auto& aval = numbers[1];
|
||||
ensure("auto element", aval==6);
|
||||
|
||||
// auto iterator (non-const)
|
||||
auto it = numbers.rbegin();
|
||||
*it += 1;
|
||||
S32 val = *it;
|
||||
ensure("auto iterator", val==10);
|
||||
}
|
||||
|
||||
// range for
|
||||
//
|
||||
// https://en.cppreference.com/w/cpp/language/range-for
|
||||
//
|
||||
// Can iterate over containers without explicit iterator
|
||||
template<> template<>
|
||||
void cpp_features_test_object_t::test<3>()
|
||||
{
|
||||
|
||||
// Traditional iterator for with container
|
||||
//
|
||||
// Problems:
|
||||
// * Have to create a new variable for the iterator, which is unrelated to the problem you're trying to solve.
|
||||
// * Redundant and somewhat fragile. Have to make sure begin() and end() are both from the right container.
|
||||
std::vector<S32> numbers{3,6,9};
|
||||
for (auto it = numbers.begin(); it != numbers.end(); ++it)
|
||||
{
|
||||
auto& n = *it;
|
||||
n *= 2;
|
||||
}
|
||||
ensure("iterator for vector", numbers[2]==18);
|
||||
|
||||
// Range for with container
|
||||
//
|
||||
// Under the hood, this is doing the same thing as the traditional
|
||||
// for loop above. Still uses begin() and end() but you don't have
|
||||
// to access them directly.
|
||||
std::vector<S32> numbersb{3,6,9};
|
||||
for (auto& n: numbersb)
|
||||
{
|
||||
n *= 2;
|
||||
}
|
||||
ensure("range for vector", numbersb[2]==18);
|
||||
|
||||
// Range for over a C-style array.
|
||||
//
|
||||
// This is handy because the language determines the range automatically.
|
||||
// Getting this right manually is a little trickier.
|
||||
S32 pows[] = {1,2,4,8,16};
|
||||
S32 sum{};
|
||||
for (const auto& v: pows)
|
||||
{
|
||||
sum += v;
|
||||
}
|
||||
ensure("for C-array", sum==31);
|
||||
}
|
||||
|
||||
// override specifier
|
||||
//
|
||||
// https://en.cppreference.com/w/cpp/language/override
|
||||
//
|
||||
// Specify that a particular class function is an override of a virtual function.
|
||||
// Benefits:
|
||||
// * Makes code somewhat easier to read by showing intent.
|
||||
// * Prevents mistakes where you think something is an override but it doesn't actually match the declaration in the parent class.
|
||||
// Drawbacks:
|
||||
// * Some compilers require that any class using override must use it consistently for all functions.
|
||||
// This makes switching a class to use override a lot more work.
|
||||
|
||||
class Foo
|
||||
{
|
||||
public:
|
||||
virtual bool is_happy() const = 0;
|
||||
};
|
||||
|
||||
class Bar: public Foo
|
||||
{
|
||||
public:
|
||||
bool is_happy() const override { return true; }
|
||||
// Override would fail: non-const declaration doesn't match parent
|
||||
// bool is_happy() override { return true; }
|
||||
// Override would fail: wrong name
|
||||
// bool is_happx() override { return true; }
|
||||
};
|
||||
|
||||
template<> template<>
|
||||
void cpp_features_test_object_t::test<4>()
|
||||
{
|
||||
Bar b;
|
||||
ensure("override", b.is_happy());
|
||||
}
|
||||
|
||||
// final
|
||||
//
|
||||
// https://en.cppreference.com/w/cpp/language/final: "Specifies that a
|
||||
// virtual function cannot be overridden in a derived class or that a
|
||||
// class cannot be inherited from."
|
||||
|
||||
class Vehicle
|
||||
{
|
||||
public:
|
||||
virtual bool has_wheels() const = 0;
|
||||
};
|
||||
|
||||
class WheeledVehicle: public Vehicle
|
||||
{
|
||||
public:
|
||||
virtual bool has_wheels() const final override { return true; }
|
||||
};
|
||||
|
||||
class Bicycle: public WheeledVehicle
|
||||
{
|
||||
public:
|
||||
// Error: can't override final version in WheeledVehicle
|
||||
// virtual bool has_wheels() override const { return true; }
|
||||
};
|
||||
|
||||
template<> template<>
|
||||
void cpp_features_test_object_t::test<5>()
|
||||
{
|
||||
Bicycle bi;
|
||||
ensure("final", bi.has_wheels());
|
||||
}
|
||||
|
||||
// deleted function declaration
|
||||
//
|
||||
// https://en.cppreference.com/w/cpp/language/function#Deleted_functions
|
||||
//
|
||||
// Typical case: copy constructor doesn't make sense for a particular class, so you want to make
|
||||
// sure the no one tries to copy-construct an instance of the class, and that the
|
||||
// compiler won't generate a copy constructor for you automatically.
|
||||
// Traditional fix is to declare a
|
||||
// copy constructor but never implement it, giving you a link-time error if anyone tries to use it.
|
||||
// Now you can explicitly declare a function to be deleted, which has at least two advantages over
|
||||
// the old way:
|
||||
// * Makes the intention clear
|
||||
// * Creates an error sooner, at compile time
|
||||
|
||||
class DoNotCopy
|
||||
{
|
||||
public:
|
||||
DoNotCopy() {}
|
||||
DoNotCopy(const DoNotCopy& ref) = delete;
|
||||
};
|
||||
|
||||
template<> template<>
|
||||
void cpp_features_test_object_t::test<6>()
|
||||
{
|
||||
DoNotCopy nc; // OK, default constructor
|
||||
//DoNotCopy nc2(nc); // No, can't copy
|
||||
//DoNotCopy nc3 = nc; // No, this also calls copy constructor (even though it looks like an assignment)
|
||||
}
|
||||
|
||||
// defaulted function declaration
|
||||
//
|
||||
// https://en.cppreference.com/w/cpp/language/function#Function_definition
|
||||
//
|
||||
// What about the complementary case to the deleted function declaration, where you want a copy constructor
|
||||
// and are happy with the default implementation the compiler will make (memberwise copy).
|
||||
// Now you can explicitly declare that too.
|
||||
// Usage: I guess it makes the intent clearer, but otherwise not obviously useful.
|
||||
class DefaultCopyOK
|
||||
{
|
||||
public:
|
||||
DefaultCopyOK(): mVal(123) {}
|
||||
DefaultCopyOK(const DefaultCopyOK&) = default;
|
||||
S32 val() const { return mVal; }
|
||||
private:
|
||||
S32 mVal;
|
||||
};
|
||||
|
||||
template<> template<>
|
||||
void cpp_features_test_object_t::test<7>()
|
||||
{
|
||||
DefaultCopyOK d; // OK
|
||||
DefaultCopyOK d2(d); // OK
|
||||
DefaultCopyOK d3 = d; // OK
|
||||
ensure("default copy d", d.val()==123);
|
||||
ensure("default copy d2", d.val()==d2.val());
|
||||
ensure("default copy d3", d.val()==d3.val());
|
||||
}
|
||||
|
||||
// initialize class members inline
|
||||
//
|
||||
// https://en.cppreference.com/w/cpp/language/data_members#Member_initialization
|
||||
//
|
||||
// Default class member values can be set where they are declared, using either brackets or =
|
||||
|
||||
// It is preferred to skip creating a constructor if all the work can be done by inline initialization:
|
||||
// http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines.html#c45-dont-define-a-default-constructor-that-only-initializes-data-members-use-in-class-member-initializers-instead
|
||||
//
|
||||
class InitInline
|
||||
{
|
||||
public:
|
||||
S32 mFoo{10};
|
||||
};
|
||||
|
||||
class InitInlineWithConstructor
|
||||
{
|
||||
public:
|
||||
// Here mFoo is not specified, so you will get the default value of 10.
|
||||
// mBar is specified, so 25 will override the default value.
|
||||
InitInlineWithConstructor():
|
||||
mBar(25)
|
||||
{}
|
||||
|
||||
// Default values set using two different styles, same effect.
|
||||
S32 mFoo{10};
|
||||
S32 mBar = 20;
|
||||
};
|
||||
|
||||
template<> template<>
|
||||
void cpp_features_test_object_t::test<8>()
|
||||
{
|
||||
InitInline ii;
|
||||
ensure("init member inline 1", ii.mFoo==10);
|
||||
|
||||
InitInlineWithConstructor iici;
|
||||
ensure("init member inline 2", iici.mFoo=10);
|
||||
ensure("init member inline 3", iici.mBar==25);
|
||||
}
|
||||
|
||||
// constexpr
|
||||
//
|
||||
// https://en.cppreference.com/w/cpp/language/constexpr
|
||||
//
|
||||
// Various things can be computed at compile time, and flagged as constexpr.
|
||||
constexpr S32 compute2() { return 2; }
|
||||
|
||||
constexpr S32 ce_factorial(S32 n)
|
||||
{
|
||||
if (n<=0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return n*ce_factorial(n-1);
|
||||
}
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void cpp_features_test_object_t::test<9>()
|
||||
{
|
||||
S32 val = compute2();
|
||||
ensure("constexpr 1", val==2);
|
||||
|
||||
// Compile-time factorial. You used to need complex templates to do something this useless.
|
||||
S32 fac5 = ce_factorial(5);
|
||||
ensure("constexpr 2", fac5==120);
|
||||
}
|
||||
|
||||
// static assert
|
||||
//
|
||||
// https://en.cppreference.com/w/cpp/language/static_assert
|
||||
//
|
||||
// You can add asserts to be checked at compile time. The thing to be checked must be a constexpr.
|
||||
// There are two forms:
|
||||
// * static_assert(expr);
|
||||
// * static_assert(expr, message);
|
||||
//
|
||||
// Currently only the 2-parameter form works on windows. The 1-parameter form needs a flag we don't set.
|
||||
|
||||
template<> template<>
|
||||
void cpp_features_test_object_t::test<10>()
|
||||
{
|
||||
// static_assert(ce_factorial(6)==720); No, needs a flag we don't currently set.
|
||||
static_assert(ce_factorial(6)==720, "bad factorial"); // OK
|
||||
}
|
||||
|
||||
// type aliases
|
||||
//
|
||||
// https://en.cppreference.com/w/cpp/language/type_alias
|
||||
//
|
||||
// You can use the "using" statement to create simpler templates that
|
||||
// are aliases for more complex ones. "Template typedef"
|
||||
|
||||
// This makes stringmap<T> an alias for std::map<std::string, T>
|
||||
template<typename T>
|
||||
using stringmap = std::map<std::string, T>;
|
||||
|
||||
template<> template<>
|
||||
void cpp_features_test_object_t::test<11>()
|
||||
{
|
||||
stringmap<S32> name_counts{ {"alice", 3}, {"bob", 2} };
|
||||
ensure("type alias", name_counts["bob"]==2);
|
||||
}
|
||||
|
||||
// Other possibilities:
|
||||
|
||||
// nullptr
|
||||
|
||||
// class enums
|
||||
|
||||
// std::unique_ptr and make_unique
|
||||
|
||||
// std::shared_ptr and make_shared
|
||||
|
||||
// lambdas
|
||||
|
||||
// perfect forwarding
|
||||
|
||||
// variadic templates
|
||||
|
||||
// std::thread
|
||||
|
||||
// std::mutex
|
||||
|
||||
// thread_local
|
||||
|
||||
// rvalue reference &&
|
||||
|
||||
// move semantics
|
||||
|
||||
// std::move
|
||||
|
||||
// string_view
|
||||
|
||||
} // namespace tut
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
#!runpy.sh
|
||||
|
||||
"""\
|
||||
|
||||
This module contains code for analyzing ViewerStats data as uploaded by the viewer.
|
||||
|
||||
$LicenseInfo:firstyear=2021&license=viewerlgpl$
|
||||
Second Life Viewer Source Code
|
||||
Copyright (C) 2021, Linden Research, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation;
|
||||
version 2.1 of the License only.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
$/LicenseInfo$
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import json
|
||||
from collections import Counter, defaultdict
|
||||
from llbase import llsd
|
||||
import io
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
|
||||
def show_stats_by_key(recs,indices,settings_sd = None):
|
||||
result = ()
|
||||
cnt = Counter()
|
||||
per_key_cnt = defaultdict(Counter)
|
||||
for r in recs:
|
||||
try:
|
||||
d = r
|
||||
for idx in indices:
|
||||
d = d[idx]
|
||||
for k,v in d.items():
|
||||
if isinstance(v,dict):
|
||||
continue
|
||||
cnt[k] += 1
|
||||
if isinstance(v,list):
|
||||
v = tuple(v)
|
||||
per_key_cnt[k][v] += 1
|
||||
except Exception as e:
|
||||
print "err", e
|
||||
print "d", d, "k", k, "v", v
|
||||
raise
|
||||
mc = cnt.most_common()
|
||||
print "========================="
|
||||
keyprefix = ""
|
||||
if len(indices)>0:
|
||||
keyprefix = ".".join(indices) + "."
|
||||
for i,m in enumerate(mc):
|
||||
k = m[0]
|
||||
bigc = m[1]
|
||||
unset_cnt = len(recs) - bigc
|
||||
kmc = per_key_cnt[k].most_common(5)
|
||||
print i, keyprefix+str(k), bigc
|
||||
if settings_sd is not None and k in settings_sd and "Value" in settings_sd[k]:
|
||||
print " ", "default",settings_sd[k]["Value"],"count",unset_cnt
|
||||
for v in kmc:
|
||||
print " ", "value",v[0],"count",v[1]
|
||||
if settings_sd is not None:
|
||||
print "Total keys in settings", len(settings_sd.keys())
|
||||
unused_keys = list(set(settings_sd.keys()) - set(cnt.keys()))
|
||||
unused_keys_non_str = [k for k in unused_keys if settings_sd[k]["Type"] != "String"]
|
||||
unused_keys_str = [k for k in unused_keys if settings_sd[k]["Type"] == "String"]
|
||||
|
||||
# Things that no one in the sample has set to a non-default value. Possible candidates for removal.
|
||||
print "\nUnused_keys_non_str", len(unused_keys_non_str)
|
||||
print "======================"
|
||||
print "\n".join(sorted(unused_keys_non_str))
|
||||
|
||||
# Strings are not currently logged, so we have no info on usage.
|
||||
print "\nString keys (usage unknown)", len(unused_keys_str)
|
||||
print "======================"
|
||||
print "\n".join(sorted(unused_keys_str))
|
||||
|
||||
# Things that someone has set but that aren't recognized settings.
|
||||
unrec_keys = list(set(cnt.keys()) - set(settings_sd.keys()))
|
||||
print "\nUnrecognized keys", len(unrec_keys)
|
||||
print "======================"
|
||||
print "\n".join(sorted(unrec_keys))
|
||||
|
||||
result = (settings_sd.keys(), unused_keys_str, unused_keys_non_str, unrec_keys)
|
||||
return result
|
||||
|
||||
def parse_settings_xml(fname):
|
||||
# assume we're in scripts/metrics
|
||||
fname = "../../indra/newview/app_settings/" + fname
|
||||
with open(fname,"r") as f:
|
||||
contents = f.read()
|
||||
return llsd.parse_xml(contents)
|
||||
|
||||
def read_raw_settings_xml(fname):
|
||||
# assume we're in scripts/metrics
|
||||
fname = "../../indra/newview/app_settings/" + fname
|
||||
contents = None
|
||||
with open(fname,"r") as f:
|
||||
contents = f.read()
|
||||
return contents
|
||||
|
||||
def write_settings_xml(fname, contents):
|
||||
# assume we're in scripts/metrics
|
||||
fname = "../../indra/newview/app_settings/" + fname
|
||||
with open(fname,"w") as f:
|
||||
f.write(llsd.format_pretty_xml(contents))
|
||||
f.close()
|
||||
|
||||
def write_raw_settings_xml(fname, string):
|
||||
# assume we're in scripts/metrics
|
||||
fname = "../../indra/newview/app_settings/" + fname
|
||||
with io.open(fname,"w", newline='\n') as f:
|
||||
f.write(string.decode('latin1'))
|
||||
f.close()
|
||||
|
||||
def remove_settings(string, to_remove):
|
||||
for r in to_remove:
|
||||
subs_str = r"<key>" + r + r"<.*?</map>\n"
|
||||
string = re.sub(subs_str,"",string,flags=re.S|re.DOTALL)
|
||||
return string
|
||||
|
||||
def get_used_strings(root_dir):
|
||||
used_str = set()
|
||||
skipped_ext = set()
|
||||
for dir_name, sub_dir_list, file_list in os.walk(root_dir):
|
||||
for fname in file_list:
|
||||
if fname in ["settings.xml", "settings.xml.edit", "settings_per_account.xml"]:
|
||||
print "skip", fname
|
||||
continue
|
||||
(base,ext) = os.path.splitext(fname)
|
||||
#if ext not in [".cpp", ".hpp", ".h", ".xml"]:
|
||||
# skipped_ext.add(ext)
|
||||
# continue
|
||||
|
||||
full_name = os.path.join(dir_name,fname)
|
||||
|
||||
with open(full_name,"r") as f:
|
||||
#print full_name
|
||||
lines = f.readlines()
|
||||
for l in lines:
|
||||
ms = re.findall(r'[>\"]([A-Za-z0-9_]+)[\"<]',l)
|
||||
for m in ms:
|
||||
#print "used_str",m
|
||||
used_str.add(m)
|
||||
print "skipped extensions", skipped_ext
|
||||
print "got used_str", len(used_str)
|
||||
return used_str
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
parser = argparse.ArgumentParser(description="process tab-separated table containing viewerstats logs")
|
||||
parser.add_argument("--verbose", action="store_true",help="verbose flag")
|
||||
parser.add_argument("--preferences", action="store_true", help="analyze preference info")
|
||||
parser.add_argument("--remove_unused", action="store_true", help="remove unused preferences")
|
||||
parser.add_argument("--column", help="name of column containing viewerstats info")
|
||||
parser.add_argument("infiles", nargs="+", help="name of .tsv files to process")
|
||||
args = parser.parse_args()
|
||||
|
||||
for fname in args.infiles:
|
||||
print "process", fname
|
||||
df = pd.read_csv(fname,sep='\t')
|
||||
#print "DF", df.describe()
|
||||
jstrs = df['RAW_LOG:BODY']
|
||||
#print "JSTRS", jstrs.describe()
|
||||
recs = []
|
||||
for i,jstr in enumerate(jstrs):
|
||||
recs.append(json.loads(jstr))
|
||||
show_stats_by_key(recs,[])
|
||||
show_stats_by_key(recs,["agent"])
|
||||
if args.preferences:
|
||||
print "\nSETTINGS.XML"
|
||||
settings_sd = parse_settings_xml("settings.xml")
|
||||
#for skey,svals in settings_sd.items():
|
||||
# print skey, "=>", svals
|
||||
(all_str,_,_,_) = show_stats_by_key(recs,["preferences","settings"],settings_sd)
|
||||
print
|
||||
|
||||
#print "\nSETTINGS_PER_ACCOUNT.XML"
|
||||
#settings_pa_sd = parse_settings_xml("settings_per_account.xml")
|
||||
#show_stats_by_key(recs,["preferences","settings_per_account"],settings_pa_sd)
|
||||
|
||||
if args.remove_unused:
|
||||
# walk codebase looking for strings
|
||||
all_str_set = set(all_str)
|
||||
used_strings = get_used_strings("../../indra")
|
||||
used_strings_set = set(used_strings)
|
||||
unref_strings = all_str_set-used_strings_set
|
||||
# Some settings names are generated by appending to a prefix. Need to look for this case.
|
||||
prefix_used = set()
|
||||
print "checking unref_strings", len(unref_strings)
|
||||
for u in unref_strings:
|
||||
for k in range(6,len(u)):
|
||||
prefix = u[0:k]
|
||||
if prefix in all_str_set and prefix in used_strings_set:
|
||||
prefix_used.add(u)
|
||||
#print "PREFIX_USED",u,prefix
|
||||
print "PREFIX_USED", len(prefix_used), ",".join(list(prefix_used))
|
||||
print
|
||||
unref_strings = unref_strings - prefix_used
|
||||
|
||||
print "\nUNREF_IN_CODE " + str(len(unref_strings)) + "\n"
|
||||
print "\n".join(list(unref_strings))
|
||||
settings_str = read_raw_settings_xml("settings.xml")
|
||||
# Do this via direct string munging to generate minimal changeset
|
||||
settings_edited = remove_settings(settings_str,unref_strings)
|
||||
write_raw_settings_xml("settings.xml.edit",settings_edited)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue