Skip to content

Add experimental support for tracking the invocations of runtime functions #11910

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Sep 18, 2017

Conversation

swiftix
Copy link
Contributor

@swiftix swiftix commented Sep 14, 2017

It allows for collecting the state of runtime function counters, which are used to determine how many times a given runtime function was called.

It is possible to get the global counters, which represent the total number of invocations, or per-object counters, which represent the number of runtime functions calls for a specific object.

The APIs are implemented in the runtime library and are exposed as Swift APIs via the standard library. This way it is possible to write tests and benchmarks which collect very precise data about the amount of runtime functions invocations (e.g. swift_retain/swift_release) for an object or for a given code snippet.

@swiftix
Copy link
Contributor Author

swiftix commented Sep 14, 2017

@swift-ci please smoke test

@swiftix
Copy link
Contributor Author

swiftix commented Sep 14, 2017

@moiseev Max, could you have a look at the stdlib part?

@swiftix
Copy link
Contributor Author

swiftix commented Sep 14, 2017

@aschwaighofer Oh, I see, you are right!

@swiftix swiftix requested a review from moiseev September 14, 2017 02:28
…cations of runtime functions

It allows for collecting the state of runtime function counters, which are used to determine how many times a given runtime function was called.

It is possible to get the global counters, which represent the total number of invocations, or per-object counters, which represent the number of runtime functions calls for a specific object.
@swiftix swiftix force-pushed the resilience-performance1 branch from 4d7ff14 to 4b2c6c0 Compare September 14, 2017 23:43
@swiftix
Copy link
Contributor Author

swiftix commented Sep 14, 2017

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 824c085a537e30d57a15765a8513ed4b6fcc2280

@swiftix
Copy link
Contributor Author

swiftix commented Sep 14, 2017

@swift-ci please test

@swiftix swiftix requested a review from gottesmm September 14, 2017 23:52
@swiftix
Copy link
Contributor Author

swiftix commented Sep 14, 2017

@gottesmm Michael, do you want to have a look at this attempt to provide runtime function counters?

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 4b2c6c0

@swiftix
Copy link
Contributor Author

swiftix commented Sep 15, 2017

@swift-ci please smoke test OS X

1 similar comment
@swiftix
Copy link
Contributor Author

swiftix commented Sep 15, 2017

@swift-ci please smoke test OS X

@swiftix
Copy link
Contributor Author

swiftix commented Sep 15, 2017

@swift-ci please test OS X

1 similar comment
@swiftix
Copy link
Contributor Author

swiftix commented Sep 15, 2017

@swift-ci please test OS X

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - a4d5cad

@swiftix
Copy link
Contributor Author

swiftix commented Sep 15, 2017

@swift-ci please test OS X

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - a4d5cad

@swiftix
Copy link
Contributor Author

swiftix commented Sep 15, 2017

@swift-ci please test OS X

1 similar comment
@swiftix
Copy link
Contributor Author

swiftix commented Sep 15, 2017

@swift-ci please test OS X

@swiftix
Copy link
Contributor Author

swiftix commented Sep 15, 2017

@swift-ci please smoke test linux

… and initialization of static or stack-promoted objects
@swiftix
Copy link
Contributor Author

swiftix commented Sep 15, 2017

@swift-ci please smoke test

static public func dumpObjectsRuntimeFunctionPointers()

@_silgen_name("setGlobalRuntimeFunctionCountersUpdateHandler")
static public func setGlobalRuntimeFunctionCountersUpdateHandler(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@discardableResult

let runtimeFunctionNames = _RuntimeFunctionCounters._getRuntimeFunctionNames()
let numRuntimeFunctionCounters =
_RuntimeFunctionCounters.getNumRuntimeFunctionCounters()
var runtimtFunctionNames : [String] = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/runtimt/runtime

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you already know the future length of a resulting array, calling runtimeFunctionNames.reserveCapacity(numRuntimeFunctionCounters) will help avoid reallocation.

_ value: Any,
references: inout [UnsafeRawPointer],
visitedItems: inout [ObjectIdentifier : Int]
) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Common style is to outdent (which turns out to be the term for 'un-indent') this line.


// Recursively visit the children of the current value.
var currentIndex = mirror.children.startIndex
for _ in 0..<count {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't you just for child in mirror.children?

}
}

/// This is a namespace for runtime functions related to management
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// is used for doc comments only.

init()

/// Dump the current state of all counters.
func dump(skipUnchanged: Bool)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this?

protocol _RuntimeFunctionCountersStats {
  func dump<T : TextOutputStream>(skipUnchanged: Bool, to: inout T)
  func dumpDiff<T : TextOutputStream>(_ after: Self, skipUnchanged: Bool, to: inout T)
}

And then:

extension _RuntimeFunctionCounterStats {
  public func dump(skipUnchanged: Bool) {
    dump(skipUnchanged: skipUnchanged, to: &_StdOut)
  }
}

extension _RuntimeFunctionCountersStats : CustomDebugStringConvertible {
  public var debugDescription: String {
    var result = ""
    dump(skipUnchanged: true, to: &result)
    return result
  }
}

)

// Use counter name as index.
subscript(_ index: String) -> UInt32 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

index is misleading here. Just call it counterName =)

public // @testable
func enableRuntimeFunctionCountersUpdates(
mode: (globalMode: Bool, perObjectMode: Bool) = (true, true)) {
_ = _RuntimeFunctionCounters.setGlobalRuntimeFunctionCountersMode(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_ = should become unnecessary once the function is marked with a @discardableResult attribute.

/// counters related to a given object.
public // @testable
func _measureRuntimeFunctionCountersDiffs(
objects: [UnsafeRawPointer], _ f: () -> Void) ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

f is not very descriptive. body perhaps? At least it has some part of the overall meaning...

f()
// Disable counters updates.
_RuntimeFunctionCounters.enableRuntimeFunctionCountersUpdates(
mode: (globalMode: false, perObjectMode: false))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indent this line please.

@swiftix
Copy link
Contributor Author

swiftix commented Sep 15, 2017

@moiseev Max, thanks a lot for your review! I addressed all of your comments and pushed a new version. Waiting for @gottesmm to review the C++ part of the PR.

@swiftix
Copy link
Contributor Author

swiftix commented Sep 15, 2017

@swift-ci please smoke test

@gparker42
Copy link
Contributor

I don't see any thread-safety here. Are these counters supposed to be atomic?

@swiftix
Copy link
Contributor Author

swiftix commented Sep 15, 2017

@gparker42 I've put into the comments that we may want to make those counters atomic. But in principle, the main motivation for them is to write some tests and perform some performance investigations to track e.g. the number of ARC operations being performed during the execution of a given stdlib call like Array.append,Dictionary.get or something like this. For this kind of use-cases, we don't need to be thread-safe IMHO. WDYT?

Copy link
Contributor

@gottesmm gottesmm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bunch of comments. Most of it comes down to is this a c++ or a c api and also can you use a .def file? The way you are using macros here is very unstructured, using a .def will be cleaner.


namespace swift {
struct HeapObject;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} // end anonymous namespace

#if !defined(NDEBUG)

/// The name of a helper function for tracking the calls of a runtime function.
#define SWIFT_RT_TRACK_INVOCATION_NAME(RT_FUNCTION, OBJ) \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a bunch of the parameters here, you have the OBJ parameter but you don't use it. Why is it still here?


// Define counters used for tracking the total number of invocations of runtime
// functions.
struct RuntimeFunctionCountersState {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is your intention here to have a c++ or a c API. If you want this to be a truly portable c API, there are a bunch of changes that would be necessary to achieve that.

/// Get the runtime object state associated with an object and store it
/// into the result.
extern "C" void
getObjectRuntimeFunctionCounters(swift::HeapObject *object,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you using a c++ reference with an extern "C". Seems weird.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is better, but you are still using C++ namespaces. If this is intended to be a C API, these should all be typedefs of void * or of an anonymous C struct.

RT_FUNCTION_TO_TRACK(object, swift_allocObject) \
RT_FUNCTION_TO_TRACK(object, swift_deallocObject) \
RT_FUNCTION_TO_TRACK(object, swift_initStackObject) \
RT_FUNCTION_TO_TRACK(object, swift_initStaticObject) \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we have some sort of .def file that you could just include?

struct HeapObject;
}

/// Instrument the runtime functions only if it is a build with
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Instrument the runtime functions only if we are building with assertions enabled."


/// The name of the counter for tracking the calls of a runtime function.
#define SWIFT_RT_FUNCTION_INVOCATION_COUNTER_NAME(RT_FUNCTION_NAME) \
invocationCounter_##RT_FUNCTION_NAME
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this exposed in the header. This seems like an implementation detail.

/// The object state cache mapping objects to the collected state associated with
/// them.
/// TODO: Do we need to make it thread-safe?
static llvm::DenseMap<HeapObject *, RuntimeFunctionCountersState> RuntimeObjectStateCache;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion: just put in a lock.

static llvm::DenseMap<HeapObject *, RuntimeFunctionCountersState> RuntimeObjectStateCache;

/// Define names of runtime functions.
#undef RT_FUNCTION_TO_TRACK
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you undefing this? This would be much simpler with a .def file.

@swiftix
Copy link
Contributor Author

swiftix commented Sep 15, 2017

@gottesmm Thanks for the review, Michael! I pushed a new version where I addressed most of your comments.

@swiftix
Copy link
Contributor Author

swiftix commented Sep 16, 2017

@swift-ci please smoke test

@swiftix
Copy link
Contributor Author

swiftix commented Sep 16, 2017

@swift-ci please smoke test

@swiftix
Copy link
Contributor Author

swiftix commented Sep 16, 2017

@swift-ci please smoke test

1 similar comment
@swiftix
Copy link
Contributor Author

swiftix commented Sep 16, 2017

@swift-ci please smoke test

@swiftix
Copy link
Contributor Author

swiftix commented Sep 17, 2017

@gottesmm Michael, is there anything still missing before I merge it?
The atomicity handling can be added later if we'd really need it (see my comments above).

@gottesmm
Copy link
Contributor

I am re-reading now.

Copy link
Contributor

@gottesmm gottesmm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks better. We generally put the #define next to the #include when using XMACRO lists. I pointed out a few areas where this happens. Can you fix those and the rest?

} // end namespace swift

/// The name of a helper function for tracking the calls of a runtime function.
#define SWIFT_RT_TRACK_INVOCATION_NAME(RT_FUNCTION) \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this outside the #if !defined(NDEBUG)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it is used also by the RuntimeInvocationsTracking.cpp, which is compiled always, even if NDEBUG is defined.

/// Get the runtime object state associated with an object and store it
/// into the result.
extern "C" void
getObjectRuntimeFunctionCounters(swift::HeapObject *object,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is better, but you are still using C++ namespaces. If this is intended to be a C API, these should all be typedefs of void * or of an anonymous C struct.

/// FUNCTION_TO_TRACK(Id)
/// Id is the name of the runtime function to be tracked.

/// Set of runtime functions that whose invocations need to be tracked.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes all of the code look much nicer! Looks really nice! One more suggestion: there is no reason why one should include this if one doesn't define FUNCTION_TO_TRACK. I would add:

#ifndef FUNCTION_TO_TRACK
#error "Must define FUNCTION_TO_TRACK to include RuntimeInvocationsTracking.def"
#endif

}

/// Define how to dump the counter for a given runtime function.
#define FUNCTION_TO_TRACK(RT_FUNCTION) \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally put this /right/ next to the #include.

So I would put it like so:

static void dumpRuntimeCounters(...) {
uint32_t tmp = 0;
#define FUNCTION_TO_TRACK(RT_FUNCTION) \
   ... \
   ...
#include "RuntimeInvocationsTracking.def"
}

/// Return the names of the runtime functions being tracked.
/// Their order is the same as the order of the counters in the
/// RuntimeObjectState structure.
SWIFT_RT_ENTRY_VISIBILITY
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these null terminated? If so, you should document that.

GlobalRuntimeFunctionCountersUpdateHandler;

/// Define offset for each function being tracked.
#define FUNCTION_TO_TRACK(RT_FUNCTION) \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this next to the include of RuntimeInvocationsTracking.def in the function itself.

… builds

No runtime function calls will be tracked in this case, but the APIs for reading them are still available. This way there is no need to recompile Swift clients if they are linked against different builds of the standard library and runtime library.
@swiftix
Copy link
Contributor Author

swiftix commented Sep 18, 2017

@swift-ci please smoke test

@gottesmm
Copy link
Contributor

LGTM

@swiftix
Copy link
Contributor Author

swiftix commented Sep 18, 2017

Big thanks to @gottesmm for his thorough review!

@swiftix
Copy link
Contributor Author

swiftix commented Sep 18, 2017

@swift-ci please test

1 similar comment
@swiftix
Copy link
Contributor Author

swiftix commented Sep 18, 2017

@swift-ci please test

@swiftix swiftix merged commit 937352a into swiftlang:master Sep 18, 2017
swiftix added a commit to swiftix/swift that referenced this pull request Oct 24, 2017
- Use SWIFT_RUNTIME_EXPORT instead of SWIFT_RT_ENTRY_VISIBILITY for exposed functions
- Use `_swift_`  prefixes on the names of exposed functions
- Make the global counters and per-object counters cache thread-safe by using locks
swiftix added a commit that referenced this pull request Oct 25, 2017
Address post-commit review comments on PR #11910
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants