Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F81799145
MultimeterControl.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sun, Sep 8, 00:04
Size
7 KB
Mime Type
text/x-php
Expires
Tue, Sep 10, 00:04 (2 d)
Engine
blob
Format
Raw Data
Handle
20620673
Attached To
rPH Phabricator
MultimeterControl.php
View Options
<?php
final
class
MultimeterControl
extends
Phobject
{
private
static
$instance
;
private
$events
=
array
();
private
$sampleRate
;
private
$pauseDepth
;
private
$eventViewer
;
private
$eventContext
;
private
function
__construct
()
{
// Private.
}
public
static
function
newInstance
()
{
$instance
=
new
MultimeterControl
();
// NOTE: We don't set the sample rate yet. This allows the multimeter to
// be initialized and begin recording events, then make a decision about
// whether the page will be sampled or not later on (once we've loaded
// enough configuration).
self
::
$instance
=
$instance
;
return
self
::
getInstance
();
}
public
static
function
getInstance
()
{
return
self
::
$instance
;
}
public
function
isActive
()
{
return
(
$this
->
sampleRate
!==
0
)
&&
(
$this
->
pauseDepth
==
0
);
}
public
function
setSampleRate
(
$rate
)
{
if
(
$rate
&&
(
mt_rand
(
1
,
$rate
)
==
$rate
))
{
$sample_rate
=
$rate
;
}
else
{
$sample_rate
=
0
;
}
$this
->
sampleRate
=
$sample_rate
;
return
;
}
public
function
pauseMultimeter
()
{
$this
->
pauseDepth
++;
return
$this
;
}
public
function
unpauseMultimeter
()
{
if
(!
$this
->
pauseDepth
)
{
throw
new
Exception
(
pht
(
'Trying to unpause an active multimeter!'
));
}
$this
->
pauseDepth
--;
return
$this
;
}
public
function
newEvent
(
$type
,
$label
,
$cost
)
{
if
(!
$this
->
isActive
())
{
return
null
;
}
$event
=
id
(
new
MultimeterEvent
())
->
setEventType
(
$type
)
->
setEventLabel
(
$label
)
->
setResourceCost
(
$cost
)
->
setEpoch
(
PhabricatorTime
::
getNow
());
$this
->
events
[]
=
$event
;
return
$event
;
}
public
function
saveEvents
()
{
if
(!
$this
->
isActive
())
{
return
;
}
$events
=
$this
->
events
;
if
(!
$events
)
{
return
;
}
if
(
$this
->
sampleRate
===
null
)
{
throw
new
PhutilInvalidStateException
(
'setSampleRate'
);
}
$this
->
addServiceEvents
();
// Don't sample any of this stuff.
$this
->
pauseMultimeter
();
$use_scope
=
AphrontWriteGuard
::
isGuardActive
();
if
(
$use_scope
)
{
$unguarded
=
AphrontWriteGuard
::
beginScopedUnguardedWrites
();
}
else
{
AphrontWriteGuard
::
allowDangerousUnguardedWrites
(
true
);
}
$caught
=
null
;
try
{
$this
->
writeEvents
();
}
catch
(
Exception
$ex
)
{
$caught
=
$ex
;
}
if
(
$use_scope
)
{
unset
(
$unguarded
);
}
else
{
AphrontWriteGuard
::
allowDangerousUnguardedWrites
(
false
);
}
$this
->
unpauseMultimeter
();
if
(
$caught
)
{
throw
$caught
;
}
}
private
function
writeEvents
()
{
if
(
PhabricatorEnv
::
isReadOnly
())
{
return
;
}
$events
=
$this
->
events
;
$random
=
Filesystem
::
readRandomBytes
(
32
);
$request_key
=
PhabricatorHash
::
digestForIndex
(
$random
);
$host_id
=
$this
->
loadHostID
(
php_uname
(
'n'
));
$context_id
=
$this
->
loadEventContextID
(
$this
->
eventContext
);
$viewer_id
=
$this
->
loadEventViewerID
(
$this
->
eventViewer
);
$label_map
=
$this
->
loadEventLabelIDs
(
mpull
(
$events
,
'getEventLabel'
));
foreach
(
$events
as
$event
)
{
$event
->
setRequestKey
(
$request_key
)
->
setSampleRate
(
$this
->
sampleRate
)
->
setEventHostID
(
$host_id
)
->
setEventContextID
(
$context_id
)
->
setEventViewerID
(
$viewer_id
)
->
setEventLabelID
(
$label_map
[
$event
->
getEventLabel
()])
->
save
();
}
}
public
function
setEventContext
(
$event_context
)
{
$this
->
eventContext
=
$event_context
;
return
$this
;
}
public
function
getEventContext
()
{
return
$this
->
eventContext
;
}
public
function
setEventViewer
(
$viewer
)
{
$this
->
eventViewer
=
$viewer
;
return
$this
;
}
private
function
loadHostID
(
$host
)
{
$map
=
$this
->
loadDimensionMap
(
new
MultimeterHost
(),
array
(
$host
));
return
idx
(
$map
,
$host
);
}
private
function
loadEventViewerID
(
$viewer
)
{
$map
=
$this
->
loadDimensionMap
(
new
MultimeterViewer
(),
array
(
$viewer
));
return
idx
(
$map
,
$viewer
);
}
private
function
loadEventContextID
(
$context
)
{
$map
=
$this
->
loadDimensionMap
(
new
MultimeterContext
(),
array
(
$context
));
return
idx
(
$map
,
$context
);
}
private
function
loadEventLabelIDs
(
array
$labels
)
{
return
$this
->
loadDimensionMap
(
new
MultimeterLabel
(),
$labels
);
}
private
function
loadDimensionMap
(
MultimeterDimension
$table
,
array
$names
)
{
$hashes
=
array
();
foreach
(
$names
as
$name
)
{
$hashes
[]
=
PhabricatorHash
::
digestForIndex
(
$name
);
}
$objects
=
$table
->
loadAllWhere
(
'nameHash IN (%Ls)'
,
$hashes
);
$map
=
mpull
(
$objects
,
'getID'
,
'getName'
);
$need
=
array
();
foreach
(
$names
as
$name
)
{
if
(
isset
(
$map
[
$name
]))
{
continue
;
}
$need
[
$name
]
=
$name
;
}
foreach
(
$need
as
$name
)
{
$object
=
id
(
clone
$table
)
->
setName
(
$name
)
->
save
();
$map
[
$name
]
=
$object
->
getID
();
}
return
$map
;
}
private
function
addServiceEvents
()
{
$events
=
PhutilServiceProfiler
::
getInstance
()->
getServiceCallLog
();
foreach
(
$events
as
$event
)
{
$type
=
idx
(
$event
,
'type'
);
switch
(
$type
)
{
case
'exec'
:
$this
->
newEvent
(
MultimeterEvent
::
TYPE_EXEC_TIME
,
$label
=
$this
->
getLabelForCommandEvent
(
$event
[
'command'
]),
(
1000000
*
$event
[
'duration'
]));
break
;
}
}
}
private
function
getLabelForCommandEvent
(
$command
)
{
$argv
=
preg_split
(
'/
\s
+/'
,
$command
);
$bin
=
array_shift
(
$argv
);
$bin
=
basename
(
$bin
);
$bin
=
trim
(
$bin
,
'"
\'
'
);
// It's important to avoid leaking details about command parameters,
// because some may be sensitive. Given this, it's not trivial to
// determine which parts of a command are arguments and which parts are
// flags.
// Rather than try too hard for now, just whitelist some workflows that we
// know about and record everything else generically. Overall, this will
// produce labels like "pygmentize" or "git log", discarding all flags and
// arguments.
$workflows
=
array
(
'git'
=>
array
(
'log'
=>
true
,
'for-each-ref'
=>
true
,
'pull'
=>
true
,
'clone'
=>
true
,
'fetch'
=>
true
,
'cat-file'
=>
true
,
'init'
=>
true
,
'config'
=>
true
,
'remote'
=>
true
,
'rev-parse'
=>
true
,
'diff'
=>
true
,
'ls-tree'
=>
true
,
),
'svn'
=>
array
(
'log'
=>
true
,
'diff'
=>
true
,
),
'hg'
=>
array
(
'log'
=>
true
,
'locate'
=>
true
,
'pull'
=>
true
,
'clone'
=>
true
,
'init'
=>
true
,
'diff'
=>
true
,
'cat'
=>
true
,
'files'
=>
true
,
),
'svnadmin'
=>
array
(
'create'
=>
true
,
),
);
$workflow
=
null
;
$candidates
=
idx
(
$workflows
,
$bin
);
if
(
$candidates
)
{
foreach
(
$argv
as
$arg
)
{
if
(
isset
(
$candidates
[
$arg
]))
{
$workflow
=
$arg
;
break
;
}
}
}
if
(
$workflow
)
{
return
'bin.'
.
$bin
.
' '
.
$workflow
;
}
else
{
return
'bin.'
.
$bin
;
}
}
}
Event Timeline
Log In to Comment