Skip to content
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

Add obelisk profiling feature #654

Merged
merged 45 commits into from
Mar 13, 2020
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
eea6d5b
Move findProjectAssets to Obelisk.Command.Project
matthewbauer Feb 10, 2020
383781d
Expose profiled in obelisk
matthewbauer Feb 10, 2020
53d3dff
Add ob profile command
matthewbauer Feb 11, 2020
0be7a17
Store ob profile data in profile/ directory
matthewbauer Feb 11, 2020
ab48708
Merge remote-tracking branch 'origin/develop' into add-obelisk-profil…
matthewbauer Feb 11, 2020
78d05a2
ob profile: Use finally to write profiling data
matthewbauer Feb 11, 2020
c0618d5
Don’t auto profile Main module
matthewbauer Feb 11, 2020
419f5e4
Don’t pass -f to nix when running findProjectAssets
matthewbauer Feb 11, 2020
b32206b
Wrap obRunExpr in parens
matthewbauer Feb 11, 2020
7a58b16
Move ob profile to own command
matthewbauer Feb 11, 2020
5396971
Add option to set profile output path
matthewbauer Feb 11, 2020
63b9ff5
Use setCwd and relative paths in findProjectAssets
matthewbauer Feb 11, 2020
1243cc8
Pass port and assets to ob profile as arguments
matthewbauer Feb 11, 2020
f882d63
Use nix-build to make Ob Profile binary
matthewbauer Feb 11, 2020
9ec5d2b
Avoid using maybe for filepath in ob profile
matthewbauer Feb 11, 2020
396cdad
Document default value in ob profile command
matthewbauer Feb 12, 2020
30183ed
Get project root & current time for ob profile later on
matthewbauer Feb 12, 2020
6acacc7
Merge remote-tracking branch 'origin/develop' into add-obelisk-profil…
matthewbauer Feb 12, 2020
0d71538
Fix ob profile to correctly take command arguments
matthewbauer Feb 12, 2020
673c796
Avoid qualifying lens operators in Obelisk.Command.Run
matthewbauer Feb 12, 2020
4916971
Delay getting port or assets until right before the
matthewbauer Feb 12, 2020
085461b
Don’t rely on root for profile output
matthewbauer Feb 12, 2020
f14f290
Use quasiquotes for ob profile command
matthewbauer Feb 12, 2020
ba85ebf
Use fixed reflex for profiling
matthewbauer Feb 12, 2020
7ce5d5b
Set -threaded in ob profile
matthewbauer Feb 13, 2020
0743c6d
Allow passing custom RTS flags to ob profile
matthewbauer Feb 19, 2020
42524b0
Avoid maybe in ob profile rts-flags
matthewbauer Feb 19, 2020
38e68c0
Make commmand line argument handling more clear for ob profile
matthewbauer Feb 19, 2020
b245d1c
Merge remote-tracking branch 'origin/develop' into add-obelisk-profil…
matthewbauer Feb 19, 2020
2268788
Merge remote-tracking branch 'origin/develop' into add-obelisk-profil…
3noch Feb 20, 2020
92c1a4a
Wrap 'waitForProcess' to avoid System.Process imports
3noch Feb 20, 2020
a505c20
Move profile flags top level
matthewbauer Feb 20, 2020
876957b
Clean lint and style
3noch Feb 20, 2020
8a2e1ab
Move profiledObRun script to default.nix
matthewbauer Feb 20, 2020
46accb4
Move profiledObRun to __unstable__ attr
matthewbauer Feb 21, 2020
772b42e
Fix CI by removing skeletonProfiledObRun from pinBuildInputs
matthewbauer Feb 24, 2020
9fb9289
Make profileObRun a directory instead of a binary
matthewbauer Feb 24, 2020
b830877
Merge branch 'develop' into add-obelisk-profiling-feature
3noch Feb 24, 2020
3c80468
Only make $out/bin/ dir for profiledObRun
matthewbauer Feb 24, 2020
ee5b42e
Create parent directory as well in ob profile templates
matthewbauer Feb 24, 2020
795fd9d
Make ob profile work in subdirectories
3noch Feb 24, 2020
6b15a33
Fix ob profile from subdirectory
3noch Feb 24, 2020
cf06275
Don't create ob profile out directories until everything has built
3noch Feb 24, 2020
076047f
Merge remote-tracking branch 'origin/develop' into add-obelisk-profil…
3noch Mar 13, 2020
b09c540
Put ob profile ChangeLog entry in correct place
3noch Mar 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ This project's release branch is `master`. This log is written from the perspect
* `ob thunk pack` will now attempt to automatically detect if the thunk is a private or public repo. To avoid this detection, specify `--private` or `--public` manually. ([#607](https://github.com/obsidiansystems/obelisk/pull/607))
* Fix a bug in the plain git thunk loader for thunks marked as 'private' when the revision is not in the default branch. ([#648](https://github.com/obsidiansystems/obelisk/pull/648))
* Improve handling of runtime nix dependencies. This may fix some issues encountered particularly by users on systems other than NixOS.
* Add `ob profile` command to run Obelisk project with profiling. `ob
profile` works like ob run, but instead of using ghci, it builds an
executable that is built with profiling enabled.

## v0.4.0.0 - 2020-01-10

Expand Down
13 changes: 6 additions & 7 deletions default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ let
openssh
];

getReflexPlatform = sys: reflex-platform-func {
inherit iosSdkVersion config;
system = sys;
enableLibraryProfiling = profiling;
getReflexPlatform = { system, enableLibraryProfiling ? profiling }: reflex-platform-func {
inherit iosSdkVersion config system enableLibraryProfiling;

nixpkgsOverlays = [
(self: super: {
Expand Down Expand Up @@ -102,7 +100,7 @@ let
];
};

reflex-platform = getReflexPlatform system;
reflex-platform = getReflexPlatform { inherit system; };
inherit (reflex-platform) hackGet nixpkgs;
pkgs = nixpkgs;

Expand Down Expand Up @@ -261,7 +259,7 @@ in rec {
# An Obelisk project is a reflex-platform project with a predefined layout and role for each component
project = base': projectDefinition:
let
projectOut = sys: let reflexPlatformProject = (getReflexPlatform sys).project; in reflexPlatformProject (args@{ nixpkgs, ... }:
projectOut = { system, enableLibraryProfiling ? profiling }: let reflexPlatformProject = (getReflexPlatform { inherit system enableLibraryProfiling; }).project; in reflexPlatformProject (args@{ nixpkgs, ... }:
let
inherit (lib.strings) hasPrefix;
mkProject =
Expand Down Expand Up @@ -366,7 +364,7 @@ in rec {
});
in allConfig;
in (mkProject (projectDefinition args)).projectConfig);
mainProjectOut = projectOut system;
mainProjectOut = projectOut { inherit system; };
serverOn = projectInst: version: serverExe
projectInst.ghc.backend
mainProjectOut.ghcjs.frontend
Expand All @@ -376,6 +374,7 @@ in rec {
linuxExe = serverOn (projectOut "x86_64-linux");
dummyVersion = "Version number is only available for deployments";
in mainProjectOut // {
profiled = projectOut { inherit system; enableLibraryProfiling = true; };
linuxExeConfigurable = linuxExe;
linuxExe = linuxExe dummyVersion;
exe = serverOn mainProjectOut dummyVersion;
Expand Down
6 changes: 3 additions & 3 deletions dep/reflex-platform/github.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"owner": "reflex-frp",
"repo": "reflex-platform",
"branch": "master",
"branch": "bump-reflex-0-6-4-1",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Once this is merged we should bump and then we can merge here.

"private": false,
"rev": "5429278830e1555a577f2550e045ce7f7164aa65",
"sha256": "1lp86cgccmim573rarsjny5vh0ygkfp5afq7006li0k9w2sw2d4c"
"rev": "38060894cb377160d1217305334f7f11202042d4",
"sha256": "0n47fcwj9szy3aqip0smmxdsp9fr78985rdslpmx2zdlkswmm5r1"
}
1 change: 1 addition & 0 deletions lib/cliapp/src/Obelisk/CliApp.hs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ module Obelisk.CliApp
, setDelegateCtlc
, setEnvOverride
, shell
, waitForProcess
) where

import Control.Monad.Log (Severity (..))
Expand Down
11 changes: 8 additions & 3 deletions lib/cliapp/src/Obelisk/CliApp/Process.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module Obelisk.CliApp.Process
, setDelegateCtlc
, setEnvOverride
, shell
, waitForProcess
) where

import Control.Monad ((<=<), join, void)
Expand All @@ -51,7 +52,7 @@ import System.IO (Handle)
import System.IO.Streams (InputStream, handleToInputStream)
import qualified System.IO.Streams as Streams
import System.IO.Streams.Concurrent (concurrentMerge)
import System.Process (CreateProcess, ProcessHandle, StdStream (CreatePipe), std_err, std_out, waitForProcess)
import System.Process (CreateProcess, ProcessHandle, StdStream (CreatePipe), std_err, std_out)
import qualified System.Process as Process
import qualified Data.Aeson as Aeson

Expand Down Expand Up @@ -143,7 +144,7 @@ readProcessAndLogOutput (sev_out, sev_err) process = do
outText <- liftIO $ T.decodeUtf8With lenientDecode <$> BS.hGetContents out
putLogRaw sev_out outText

liftIO (waitForProcess p) >>= \case
waitForProcess p >>= \case
ExitSuccess -> pure outText
ExitFailure code -> throwError $ review asProcessFailure $ ProcessFailure (Process.cmdspec $ _processSpec_createProcess process) code

Expand Down Expand Up @@ -218,7 +219,7 @@ withProcess process f = do -- TODO: Use bracket.

f out err -- Pass the handles to the passed function

liftIO (waitForProcess p) >>= \case
waitForProcess p >>= \case
ExitSuccess -> return (out, err)
ExitFailure code -> throwError $ review asProcessFailure $ ProcessFailure (Process.cmdspec $ _processSpec_createProcess process) code

Expand All @@ -235,6 +236,10 @@ streamToLog stream = fix $ \loop -> do
Nothing -> return ()
Just (sev, line) -> putLogRaw sev (T.decodeUtf8With lenientDecode line) >> loop

-- | Wrapper around `System.Process.waitForProcess`
waitForProcess :: MonadIO m => ProcessHandle -> m ExitCode
waitForProcess = liftIO . Process.waitForProcess

-- | Pretty print a 'CmdSpec'
reconstructCommand :: Process.CmdSpec -> Text
reconstructCommand p = case p of
Expand Down
24 changes: 23 additions & 1 deletion lib/command/src/Obelisk/Command.hs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ data ObCommand
= ObCommand_Init InitSource Bool
| ObCommand_Deploy DeployCommand
| ObCommand_Run
| ObCommand_Profile String [String]
| ObCommand_Thunk ThunkCommand
| ObCommand_Repl
| ObCommand_Watch
Expand All @@ -108,6 +109,7 @@ obCommand cfg = hsubparser
[ command "init" $ info (ObCommand_Init <$> initSource <*> initForce) $ progDesc "Initialize an Obelisk project"
, command "deploy" $ info (ObCommand_Deploy <$> deployCommand cfg) $ progDesc "Prepare a deployment for an Obelisk project"
, command "run" $ info (pure ObCommand_Run) $ progDesc "Run current project in development mode"
, command "profile" $ info (uncurry ObCommand_Profile <$> profileCommand) $ progDesc "Run current project with profiling enabled"
, command "thunk" $ info (ObCommand_Thunk <$> thunkCommand) $ progDesc "Manipulate thunk directories"
, command "repl" $ info (pure ObCommand_Repl) $ progDesc "Open an interactive interpreter"
, command "watch" $ info (pure ObCommand_Watch) $ progDesc "Watch current project for errors and warnings"
Expand All @@ -133,7 +135,7 @@ deployCommand cfg = hsubparser $ mconcat
where
platformP = hsubparser $ mconcat
[ command "android" $ info (pure (Android, [])) mempty
, command "ios" $ info ((,) <$> pure IOS <*> (fmap pure $ strArgument (metavar "TEAMID" <> help "Your Team ID - found in the Apple developer portal"))) mempty
, command "ios" $ info ((,) <$> pure IOS <*> fmap pure (strArgument (metavar "TEAMID" <> help "Your Team ID - found in the Apple developer portal"))) mempty
]

remoteBuilderParser :: Parser (Maybe RemoteBuilder)
Expand Down Expand Up @@ -183,6 +185,25 @@ data DeployInitOpts = DeployInitOpts
}
deriving Show

profileCommand :: Parser (String, [String])
profileCommand = (,)
<$> strOption
( long "output"
<> short 'o'
<> help "Base output to use for profiling output. Suffixes are added to this based on the profiling type. Defaults to a timestamped path in the profile/ directory in the project's root."
<> metavar "PATH"
<> value "profile/%Y-%m-%dT%H:%M:%S"
<> showDefault
)
<*> (words <$> strOption
( long "rts-flags"
<> help "RTS Flags to pass to the executable."
<> value "-p -hc"
<> metavar "FLAGS"
<> showDefault
))


--TODO: Result should provide normalised path and also original user input for error reporting.
thunkDirectoryParser :: Parser FilePath
thunkDirectoryParser = fmap (dropTrailingPathSeparator . normalise) . strArgument $ mconcat
Expand Down Expand Up @@ -366,6 +387,7 @@ ob = \case
DeployCommand_Update -> deployUpdate "."
DeployCommand_Test (platform, extraArgs) -> deployMobile platform extraArgs
ObCommand_Run -> run
ObCommand_Profile basePath rtsFlags -> profile basePath rtsFlags
ObCommand_Thunk tc -> case tc of
ThunkCommand_Update thunks config -> for_ thunks (updateThunkToLatest config)
ThunkCommand_Unpack thunks -> for_ thunks unpackThunk
Expand Down
30 changes: 25 additions & 5 deletions lib/command/src/Obelisk/Command/Project.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module Obelisk.Command.Project
( InitSource (..)
, findProjectObeliskCommand
, findProjectRoot
, findProjectAssets
, initProject
, nixShellRunConfig
, nixShellRunProc
Expand Down Expand Up @@ -41,7 +42,6 @@ import System.IO.Temp
import System.IO.Unsafe (unsafePerformIO)
import System.Posix (FileStatus, FileMode, CMode (..), UserID, deviceID, fileID, fileMode, fileOwner, getFileStatus, getRealUserID)
import System.Posix.Files
import System.Process (waitForProcess)

import GitHub.Data.GitData (Branch)
import GitHub.Data.Name (Name)
Expand All @@ -50,7 +50,7 @@ import Obelisk.App (MonadObelisk)
import Obelisk.CliApp
import Obelisk.Command.Nix
import Obelisk.Command.Thunk
import Obelisk.Command.Utils (nixExePath)
import Obelisk.Command.Utils (nixExePath, nixBuildExePath)

--TODO: Make this module resilient to random exceptions

Expand Down Expand Up @@ -287,7 +287,7 @@ nixShellWithPkgs root isPure chdirToRoot packageNamesAndPaths command = do
[ rawArg "root" $ toNixPath $ if chdirToRoot then "." else root
, strArg "pkgs" (T.unpack $ decodeUtf8 $ BSL.toStrict $ Json.encode packageNamesAndAbsPaths)
]
void $ liftIO $ waitForProcess ph
void $ waitForProcess ph

nixShellWithHoogle :: MonadObelisk m => FilePath -> Bool -> String -> Maybe String -> m ()
nixShellWithHoogle root isPure shell' command = do
Expand All @@ -298,12 +298,32 @@ nixShellWithHoogle root isPure shell' command = do
\userSettings = super.userSettings // { withHoogle = true; };\
\})).project.shells.${shell}"
& nixShellConfig_common . nixCmdConfig_args .~ [ strArg "shell" shell' ]
void $ liftIO $ waitForProcess ph
void $ waitForProcess ph

projectShell :: MonadObelisk m => FilePath -> Bool -> String -> Maybe String -> m ()
projectShell root isPure shellName command = do
defShellConfig <- nixShellRunConfig root isPure command
(_, _, _, ph) <- createProcess_ "runNixShellAttr" $ setCwd (Just root) $ nixShellRunProc $ defShellConfig
& nixShellConfig_common . nixCmdConfig_target . target_path ?~ "default.nix"
& nixShellConfig_common . nixCmdConfig_target . target_attr ?~ ("shells." <> shellName)
void $ liftIO $ waitForProcess ph
void $ waitForProcess ph

findProjectAssets :: MonadObelisk m => FilePath -> m Text
findProjectAssets root = do
isDerivation <- readProcessAndLogStderr Debug $ setCwd (Just root) $
proc nixExePath
[ "eval"
, "(let a = import ./. {}; in toString (a.reflex.nixpkgs.lib.isDerivation a.passthru.staticFilesImpure))"
, "--raw"
-- `--raw` is not available with old nix-instantiate. It drops quotation
-- marks and trailing newline, so is very convenient for shelling out.
]
-- Check whether the impure static files are a derivation (and so must be built)
if isDerivation == "1"
then fmap T.strip $ readProcessAndLogStderr Debug $ setCwd (Just root) $ -- Strip whitespace here because nix-build has no --raw option
proc nixBuildExePath
[ "--no-out-link"
, "-E", "(import ./. {}).passthru.staticFilesImpure"
]
3noch marked this conversation as resolved.
Show resolved Hide resolved
else readProcessAndLogStderr Debug $ setCwd (Just root) $
proc nixExePath ["eval", "-f", ".", "passthru.staticFilesImpure", "--raw"]
Loading