mirror of
https://github.com/facebook/sapling.git
synced 2024-10-05 22:37:37 +03:00
Add subcommand getpath
to eden debug
Summary: Add a command to lookup the path for an inode given the inode number and eden mount path. Reviewed By: bolinfest Differential Revision: D5627411 fbshipit-source-id: 25928f506d3f48d8a6784fe81fb17fa0500d6bc9
This commit is contained in:
parent
322cad1e8a
commit
cf297e0106
@ -302,6 +302,18 @@ def do_overlay(args: argparse.Namespace):
|
||||
_display_overlay(args, overlay_dir, 1, '/')
|
||||
|
||||
|
||||
def do_getpath(args: argparse.Namespace):
|
||||
config = cmd_util.create_config(args)
|
||||
|
||||
mount, _ = get_mount_path(args.mount)
|
||||
|
||||
with config.get_thrift_client() as client:
|
||||
inodePathInfo = client.debugGetInodePath(mount, args.number)
|
||||
print('%s %s' %
|
||||
('loaded' if inodePathInfo.loaded else 'unloaded',
|
||||
os.path.normpath(os.path.join(mount, inodePathInfo.path))))
|
||||
|
||||
|
||||
def get_loaded_inode_count(inode_info):
|
||||
count = 0
|
||||
for tree in inode_info:
|
||||
@ -388,6 +400,17 @@ def setup_argparse(parser: argparse.ArgumentParser):
|
||||
help='The path to the eden mount point.')
|
||||
parser.set_defaults(func=do_overlay)
|
||||
|
||||
parser = subparsers.add_parser(
|
||||
'getpath', help='Get the eden path that corresponds to an inode number')
|
||||
parser.add_argument(
|
||||
'mount',
|
||||
help='The path to the eden mount point.')
|
||||
parser.add_argument(
|
||||
'number',
|
||||
type=int,
|
||||
help='Display information for the specified inode number.')
|
||||
parser.set_defaults(func=do_getpath)
|
||||
|
||||
parser = subparsers.add_parser(
|
||||
'unload', help='Unload unused inodes')
|
||||
parser.add_argument(
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "eden/fs/inodes/EdenMount.h"
|
||||
#include "eden/fs/inodes/FileInode.h"
|
||||
#include "eden/fs/inodes/InodeError.h"
|
||||
#include "eden/fs/inodes/InodeMap.h"
|
||||
#include "eden/fs/inodes/Overlay.h"
|
||||
#include "eden/fs/inodes/TreeInode.h"
|
||||
#include "eden/fs/model/Blob.h"
|
||||
@ -592,6 +593,36 @@ void EdenServiceHandler::debugInodeStatus(
|
||||
|
||||
inode->getDebugStatus(inodeInfo);
|
||||
}
|
||||
|
||||
void EdenServiceHandler::debugGetInodePath(
|
||||
InodePathDebugInfo& info,
|
||||
std::unique_ptr<std::string> mountPoint,
|
||||
int64_t inodeNumber) {
|
||||
auto inodeNum = static_cast<fuse_ino_t>(inodeNumber);
|
||||
auto edenMount = server_->getMount(*mountPoint);
|
||||
auto inodeMap = edenMount->getInodeMap();
|
||||
|
||||
// Check if the inode is loaded
|
||||
auto loadedData = inodeMap->lookupLoadedInode(inodeNum);
|
||||
if (loadedData == nullptr) {
|
||||
XLOG(INFO) << "looking up unloaded inode " << inodeNum;
|
||||
// If it's not, check if it's unloaded. If the inodeNum is invalid, this
|
||||
// lookup will throw
|
||||
auto unloadedData = inodeMap->lookupUnloadedInode(inodeNum);
|
||||
info.path = unloadedData.name.stringPiece().str();
|
||||
info.loaded = false;
|
||||
} else {
|
||||
// If the inode is loaded, return its path
|
||||
auto path = loadedData->getPath();
|
||||
if (path) {
|
||||
info.path = path->stringPiece().str();
|
||||
info.loaded = true;
|
||||
} else {
|
||||
throw newEdenError("missing path for loaded inode {}", inodeNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EdenServiceHandler::unloadInodeForPath(
|
||||
unique_ptr<string> mountPoint,
|
||||
std::unique_ptr<std::string> path) {
|
||||
|
@ -145,6 +145,11 @@ class EdenServiceHandler : virtual public StreamingEdenServiceSvIf,
|
||||
std::unique_ptr<std::string> mountPoint,
|
||||
std::unique_ptr<std::string> path) override;
|
||||
|
||||
void debugGetInodePath(
|
||||
InodePathDebugInfo& inodePath,
|
||||
std::unique_ptr<std::string> mountPoint,
|
||||
int64_t inodeNumber) override;
|
||||
|
||||
void unloadInodeForPath(
|
||||
std::unique_ptr<std::string> mountPoint,
|
||||
std::unique_ptr<std::string> path) override;
|
||||
|
@ -211,6 +211,11 @@ struct TreeInodeDebugInfo {
|
||||
6: i64 refcount
|
||||
}
|
||||
|
||||
struct InodePathDebugInfo {
|
||||
1: string path
|
||||
2: bool loaded
|
||||
}
|
||||
|
||||
service EdenService extends fb303.FacebookService {
|
||||
list<MountInfo> listMounts() throws (1: EdenError ex)
|
||||
void mount(1: MountInfo info) throws (1: EdenError ex)
|
||||
@ -432,6 +437,17 @@ service EdenService extends fb303.FacebookService {
|
||||
2: string path,
|
||||
) throws (1: EdenError ex)
|
||||
|
||||
/**
|
||||
* Get the InodePathDebugInfo for the inode that corresponds to the given
|
||||
* inode number. This provides the path for the inode and also indicates
|
||||
* whether the inode is currently loaded or not. Requires that the Eden
|
||||
* mountPoint be specified.
|
||||
*/
|
||||
InodePathDebugInfo debugGetInodePath(
|
||||
1: string mountPoint,
|
||||
2: i64 inodeNumber,
|
||||
) throws (1: EdenError ex)
|
||||
|
||||
/**
|
||||
* Unloads unused Inodes of a tree Node
|
||||
*/
|
||||
|
63
eden/integration/debug_getpath_test.py
Normal file
63
eden/integration/debug_getpath_test.py
Normal file
@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2016-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the BSD-style license found in the
|
||||
# LICENSE file in the root directory of this source tree. An additional grant
|
||||
# of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
from .lib import testcase, edenclient
|
||||
import os
|
||||
|
||||
# This is the name of the default repository created by EdenRepoTestBase.
|
||||
repo_name = 'main'
|
||||
|
||||
|
||||
@testcase.eden_repo_test
|
||||
class DebugGetPathTest:
|
||||
def populate_repo(self):
|
||||
self.repo.write_file('hello', 'hola\n')
|
||||
self.repo.write_file(os.path.join('dir', 'file'), 'blah\n')
|
||||
self.repo.commit('Initial commit.')
|
||||
|
||||
def test_getpath_root_inode(self):
|
||||
'''
|
||||
Test that calling `eden debug getname 1` returns the path to the eden
|
||||
mount, and indicates that the inode is loaded.
|
||||
'''
|
||||
output = self.eden.run_cmd(
|
||||
'debug',
|
||||
'getpath',
|
||||
self.mount,
|
||||
'1')
|
||||
|
||||
self.assertEqual('loaded ' + self.mount + '\n', output)
|
||||
|
||||
def test_getpath_dot_eden_inode(self):
|
||||
'''
|
||||
Test that calling `eden debug getname 2` returns the path to the .eden
|
||||
directory, and indicates that the inode is loaded.
|
||||
'''
|
||||
output = self.eden.run_cmd(
|
||||
'debug',
|
||||
'getpath',
|
||||
self.mount,
|
||||
'2')
|
||||
|
||||
self.assertEqual(
|
||||
'loaded ' + os.path.join(self.mount, ".eden") + '\n',
|
||||
output)
|
||||
|
||||
def test_getpath_invalid_inode(self):
|
||||
'''
|
||||
Test that calling `eden debug getname 1234` raises an error since
|
||||
1234 is not a valid inode number
|
||||
'''
|
||||
with self.assertRaises(edenclient.EdenCommandError) as context:
|
||||
self.eden.run_cmd(
|
||||
'debug',
|
||||
'getpath',
|
||||
self.mount,
|
||||
'1234')
|
||||
self.assertIn('unknown inode number 1234', str(context.exception))
|
Loading…
Reference in New Issue
Block a user